From: Paul Mackerras Date: Fri, 1 Jan 2021 08:52:59 +0000 (+1100) Subject: Merge pull request #205 from pali/pppoe-rename X-Git-Tag: ppp-2.4.9~17 X-Git-Url: https://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=5b50b420fcd4708b6d5bdc55087c4c9fe06d215d;hp=0f9bd9807d92d624782fa3bc3d2abfb305edf7ee Merge pull request #205 from pali/pppoe-rename Rename rp-pppoe.so plugin to pppoe.so --- diff --git a/README.pppoe b/README.pppoe index 9f4e5cd..2909e13 100644 --- a/README.pppoe +++ b/README.pppoe @@ -52,7 +52,7 @@ to connect to their ISP who is providing PPPoE based services. 4. Add the following line to /etc/ppp/options: - plugin rp-pppoe.so + plugin pppoe.so The effect of this line is simply to make "eth0", "eth1", ....,"ethx" all valid device names for pppd (just like ttyS0, diff --git a/configure b/configure index 6a55e0f..ecd1b0d 100755 --- a/configure +++ b/configure @@ -193,7 +193,7 @@ if [ -d "$ksrc" ]; then echo "Creating Makefiles." mkmkf $ksrc/Makefile.top Makefile mkmkf $ksrc/Makedefs$compiletype Makedefs.com - for dir in pppd pppstats chat pppdump pppd/plugins pppd/plugins/rp-pppoe \ + for dir in pppd pppstats chat pppdump pppd/plugins pppd/plugins/pppoe \ pppd/plugins/radius pppd/plugins/pppoatm \ pppd/plugins/pppol2tp; do mkmkf $dir/Makefile.$makext $dir/Makefile diff --git a/pppd/plugins/Makefile.linux b/pppd/plugins/Makefile.linux index 5a7bd79..e2a680e 100644 --- a/pppd/plugins/Makefile.linux +++ b/pppd/plugins/Makefile.linux @@ -12,7 +12,7 @@ BINDIR = $(DESTDIR)/sbin MANDIR = $(DESTDIR)/share/man/man8 LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION) -SUBDIRS := rp-pppoe pppoatm pppol2tp +SUBDIRS := pppoe pppoatm pppol2tp # Uncomment the next line to include the radius authentication plugin SUBDIRS += radius PLUGINS := minconn.so passprompt.so passwordfd.so winbind.so diff --git a/pppd/plugins/pppoe/.gitignore b/pppd/plugins/pppoe/.gitignore new file mode 100644 index 0000000..00c7f9c --- /dev/null +++ b/pppd/plugins/pppoe/.gitignore @@ -0,0 +1 @@ +pppoe-discovery diff --git a/pppd/plugins/pppoe/Makefile.linux b/pppd/plugins/pppoe/Makefile.linux new file mode 100644 index 0000000..473f269 --- /dev/null +++ b/pppd/plugins/pppoe/Makefile.linux @@ -0,0 +1,64 @@ +# Generated automatically from Makefile.in by configure. +#*********************************************************************** +# +# Makefile +# +# Makefile for Roaring Penguin's Linux PPPoE plugin. +# Modified for integration with pppd sources by Paul Mackerras. +# +# Copyright (C) 2001 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. +# +# $Id: Makefile.linux,v 1.8 2008/06/09 08:34:23 paulus Exp $ +#*********************************************************************** + +DESTDIR = $(INSTROOT)@DESTDIR@ +BINDIR = $(DESTDIR)/sbin +LIBDIR = $(DESTDIR)/lib/pppd/$(PPPDVERSION) + +PPPDVERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h) + +INSTALL = install +LN_S = ln -sf + +COPTS=-O2 -g +CFLAGS=$(COPTS) -I../../../include +all: pppoe.so pppoe-discovery + +pppoe-discovery: pppoe-discovery.o debug.o + $(CC) $(LDFLAGS) -o pppoe-discovery pppoe-discovery.o debug.o + +pppoe-discovery.o: pppoe-discovery.c + $(CC) $(CFLAGS) -I../../.. -c -o pppoe-discovery.o pppoe-discovery.c + +debug.o: debug.c + $(CC) $(CFLAGS) -I../../.. -c -o debug.o debug.c + +pppoe.so: plugin.o discovery.o if.o common.o + $(CC) $(LDFLAGS) -o pppoe.so -shared plugin.o discovery.o if.o common.o + +install: all + $(INSTALL) -d -m 755 $(LIBDIR) + $(INSTALL) -c -m 4550 pppoe.so $(LIBDIR) + # Symlink for backward compatibility + $(LN_S) pppoe.so $(LIBDIR)/rp-pppoe.so + $(INSTALL) -d -m 755 $(BINDIR) + $(INSTALL) -c -m 555 pppoe-discovery $(BINDIR) + +clean: + rm -f *.o *.so pppoe-discovery + +plugin.o: plugin.c + $(CC) $(CFLAGS) -I../../.. -c -o plugin.o -fPIC plugin.c + +discovery.o: discovery.c + $(CC) $(CFLAGS) -I../../.. -c -o discovery.o -fPIC discovery.c + +if.o: if.c + $(CC) $(CFLAGS) -I../../.. -c -o if.o -fPIC if.c + +common.o: common.c + $(CC) $(CFLAGS) -I../../.. -c -o common.o -fPIC common.c + diff --git a/pppd/plugins/pppoe/common.c b/pppd/plugins/pppoe/common.c new file mode 100644 index 0000000..9ea7fd6 --- /dev/null +++ b/pppd/plugins/pppoe/common.c @@ -0,0 +1,282 @@ +/*********************************************************************** +* +* 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: common.c,v 1.3 2008/06/09 08:34:23 paulus Exp $"; + +#define _GNU_SOURCE 1 +#include "pppoe.h" +#include "pppd/pppd.h" + +#include +#include +#include +#include /* for LOG_DEBUG */ + +#ifdef HAVE_UNISTD_H +#include +#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 (PPPOE_VER(packet->vertype) != 1) { + error("Invalid PPPoE version (%d)", PPPOE_VER(packet->vertype)); + return -1; + } + if (PPPOE_TYPE(packet->vertype) != 1) { + error("Invalid PPPoE type (%d)", PPPOE_TYPE(packet->vertype)); + return -1; + } + + /* Do some sanity checks on packet */ + if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */ + error("Invalid PPPoE packet length (%u)", len); + return -1; + } + + /* Step through the tags */ + curTag = packet->payload; + while (curTag - packet->payload + TAG_HDR_SIZE <= len) { + /* Alignment is not guaranteed, so do this by hand... */ + tagType = (curTag[0] << 8) + curTag[1]; + tagLen = (curTag[2] << 8) + curTag[3]; + if (tagType == TAG_END_OF_LIST) { + return 0; + } + if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { + error("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: 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.vertype = PPPOE_VER_TYPE(1, 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->hostUniq.length) { + int len = ntohs(conn->hostUniq.length); + memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); + cursor += len + TAG_HDR_SIZE; + plen += len + 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)); + info("Sent PADT"); +} + +#define EH(x) (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5] + +/* Print out a PPPOE packet for debugging */ +void pppoe_printpkt(PPPoEPacket *packet, + void (*printer)(void *, char *, ...), void *arg) +{ + int len = ntohs(packet->length); + int i, tag, tlen, text; + + switch (ntohs(packet->ethHdr.h_proto)) { + case ETH_PPPOE_DISCOVERY: + printer(arg, "PPPOE Discovery V%dT%d ", PPPOE_VER(packet->vertype), + PPPOE_TYPE(packet->vertype)); + switch (packet->code) { + case CODE_PADI: + printer(arg, "PADI"); + break; + case CODE_PADO: + printer(arg, "PADO"); + break; + case CODE_PADR: + printer(arg, "PADR"); + break; + case CODE_PADS: + printer(arg, "PADS"); + break; + case CODE_PADT: + printer(arg, "PADT"); + break; + default: + printer(arg, "unknown code %x", packet->code); + } + printer(arg, " session 0x%x length %d\n", ntohs(packet->session), len); + break; + case ETH_PPPOE_SESSION: + printer(arg, "PPPOE Session V%dT%d", PPPOE_VER(packet->vertype), + PPPOE_TYPE(packet->vertype)); + printer(arg, " code 0x%x session 0x%x length %d\n", packet->code, + ntohs(packet->session), len); + break; + default: + printer(arg, "Unknown ethernet frame with proto = 0x%x\n", + ntohs(packet->ethHdr.h_proto)); + } + + printer(arg, " dst %02x:%02x:%02x:%02x:%02x:%02x ", EH(packet->ethHdr.h_dest)); + printer(arg, " src %02x:%02x:%02x:%02x:%02x:%02x\n", EH(packet->ethHdr.h_source)); + if (ntohs(packet->ethHdr.h_proto) != ETH_PPPOE_DISCOVERY) + return; + + for (i = 0; i + TAG_HDR_SIZE <= len; i += tlen) { + tag = (packet->payload[i] << 8) + packet->payload[i+1]; + tlen = (packet->payload[i+2] << 8) + packet->payload[i+3]; + if (i + tlen + TAG_HDR_SIZE > len) + break; + text = 0; + i += TAG_HDR_SIZE; + printer(arg, " ["); + switch (tag) { + case TAG_END_OF_LIST: + printer(arg, "end-of-list"); + break; + case TAG_SERVICE_NAME: + printer(arg, "service-name"); + text = 1; + break; + case TAG_AC_NAME: + printer(arg, "AC-name"); + text = 1; + break; + case TAG_HOST_UNIQ: + printer(arg, "host-uniq"); + break; + case TAG_AC_COOKIE: + printer(arg, "AC-cookie"); + break; + case TAG_VENDOR_SPECIFIC: + printer(arg, "vendor-specific"); + break; + case TAG_RELAY_SESSION_ID: + printer(arg, "relay-session-id"); + break; + case TAG_PPP_MAX_PAYLOAD: + printer(arg, "PPP-max-payload"); + break; + case TAG_SERVICE_NAME_ERROR: + printer(arg, "service-name-error"); + text = 1; + break; + case TAG_AC_SYSTEM_ERROR: + printer(arg, "AC-system-error"); + text = 1; + break; + case TAG_GENERIC_ERROR: + printer(arg, "generic-error"); + text = 1; + break; + default: + printer(arg, "unknown tag 0x%x", tag); + } + if (tlen) { + if (text) + printer(arg, " %.*v", tlen, &packet->payload[i]); + else if (tlen <= 32) + printer(arg, " %.*B", tlen, &packet->payload[i]); + else + printer(arg, " %.32B... (length %d)", + &packet->payload[i], tlen); + } + printer(arg, "]"); + } + printer(arg, "\n"); +} + +void pppoe_log_packet(const char *prefix, PPPoEPacket *packet) +{ + init_pr_log(prefix, LOG_DEBUG); + pppoe_printpkt(packet, pr_log, NULL); + end_pr_log(); +} diff --git a/pppd/plugins/pppoe/config.h b/pppd/plugins/pppoe/config.h new file mode 100644 index 0000000..a708859 --- /dev/null +++ b/pppd/plugins/pppoe/config.h @@ -0,0 +1,129 @@ +/* config.h. Generated automatically by configure. */ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if the setvbuf function takes the buffering type as its second + argument and the buffer pointer as the third, as on System V + before release 3. */ +/* #undef SETVBUF_REVERSED */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +#define HAVE_STRUCT_SOCKADDR_LL 1 + +/* The number of bytes in a unsigned int. */ +#define SIZEOF_UNSIGNED_INT 4 + +/* The number of bytes in a unsigned long. */ +#define SIZEOF_UNSIGNED_LONG 4 + +/* The number of bytes in a unsigned short. */ +#define SIZEOF_UNSIGNED_SHORT 2 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strtol function. */ +#define HAVE_STRTOL 1 + +/* Define if you have the header file. */ +#define HAVE_ASM_TYPES_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_IF_ETHER_H 1 + +/* Define if you have kernel-mode PPPoE in Linux file. */ +#define HAVE_LINUX_KERNEL_PPPOE 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_IF_PACKET_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_IF_PPPOX_H 1 + +/* Define if you have the header file. */ +#define HAVE_NET_BPF_H 1 + +/* Define if you have the header file. */ +#define HAVE_NET_IF_ARP_H 1 + +/* Define if you have the header file. */ +#define HAVE_NET_ETHERNET_H 1 + +/* Define if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINUX_IF_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_NET_IF_DL_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NET_IF_ETHER_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NET_IF_TYPES_H */ + +/* Define if you have the header file. */ +#define HAVE_NETINET_IF_ETHER_H 1 + +/* Define if you have the header file. */ +#define HAVE_NETPACKET_PACKET_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_DLPI_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the N_HDLC line discipline in linux/termios.h */ +#define HAVE_N_HDLC 1 diff --git a/pppd/plugins/pppoe/debug.c b/pppd/plugins/pppoe/debug.c new file mode 100644 index 0000000..a6e6981 --- /dev/null +++ b/pppd/plugins/pppoe/debug.c @@ -0,0 +1,145 @@ +/*********************************************************************** +* +* debug.c +* +* Implementation of user-space PPPoE redirector for Linux. +* +* Functions for printing debugging information +* +* 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: debug.c,v 1.2 2008/06/09 08:34:23 paulus Exp $"; + +#include "pppoe.h" +#include +#include +#include +#include + +/********************************************************************** +*%FUNCTION: dumpHex +*%ARGUMENTS: +* fp -- file to dump to +* buf -- buffer to dump +* len -- length of data +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Dumps buffer to fp in an easy-to-read format +***********************************************************************/ +void +dumpHex(FILE *fp, unsigned char const *buf, int len) +{ + int i; + int base; + + if (!fp) return; + + /* do NOT dump PAP packets */ + if (len >= 2 && buf[0] == 0xC0 && buf[1] == 0x23) { + fprintf(fp, "(PAP Authentication Frame -- Contents not dumped)\n"); + return; + } + + for (base=0; baselength); + + /* Sheesh... printing times is a pain... */ + struct timeval tv; + time_t now; + int millisec; + struct tm *lt; + char timebuf[256]; + + UINT16_t type = etherType(packet); + if (!fp) return; + gettimeofday(&tv, NULL); + now = (time_t) tv.tv_sec; + millisec = tv.tv_usec / 1000; + lt = localtime(&now); + strftime(timebuf, 256, "%H:%M:%S", lt); + fprintf(fp, "%s.%03d %s PPPoE ", timebuf, millisec, dir); + if (type == Eth_PPPOE_Discovery) { + fprintf(fp, "Discovery (%x) ", (unsigned) type); + } else if (type == Eth_PPPOE_Session) { + fprintf(fp, "Session (%x) ", (unsigned) type); + } else { + fprintf(fp, "Unknown (%x) ", (unsigned) type); + } + + switch(packet->code) { + case CODE_PADI: fprintf(fp, "PADI "); break; + case CODE_PADO: fprintf(fp, "PADO "); break; + case CODE_PADR: fprintf(fp, "PADR "); break; + case CODE_PADS: fprintf(fp, "PADS "); break; + case CODE_PADT: fprintf(fp, "PADT "); break; + case CODE_PADM: fprintf(fp, "PADM "); break; + case CODE_PADN: fprintf(fp, "PADN "); break; + case CODE_SESS: fprintf(fp, "SESS "); break; + } + + fprintf(fp, "sess-id %d length %d\n", + (int) ntohs(packet->session), + len); + + /* Ugly... I apologize... */ + fprintf(fp, + "SourceAddr %02x:%02x:%02x:%02x:%02x:%02x " + "DestAddr %02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned) packet->ethHdr.h_source[0], + (unsigned) packet->ethHdr.h_source[1], + (unsigned) packet->ethHdr.h_source[2], + (unsigned) packet->ethHdr.h_source[3], + (unsigned) packet->ethHdr.h_source[4], + (unsigned) packet->ethHdr.h_source[5], + (unsigned) packet->ethHdr.h_dest[0], + (unsigned) packet->ethHdr.h_dest[1], + (unsigned) packet->ethHdr.h_dest[2], + (unsigned) packet->ethHdr.h_dest[3], + (unsigned) packet->ethHdr.h_dest[4], + (unsigned) packet->ethHdr.h_dest[5]); + dumpHex(fp, packet->payload, ntohs(packet->length)); +} diff --git a/pppd/plugins/pppoe/discovery.c b/pppd/plugins/pppoe/discovery.c new file mode 100644 index 0000000..23089df --- /dev/null +++ b/pppd/plugins/pppoe/discovery.c @@ -0,0 +1,666 @@ +/*********************************************************************** +* +* discovery.c +* +* Perform PPPoE discovery +* +* Copyright (C) 1999 by Roaring Penguin Software Inc. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: discovery.c,v 1.6 2008/06/15 04:35:50 paulus Exp $"; + +#define _GNU_SOURCE 1 +#include "pppoe.h" +#include "pppd/pppd.h" +#include "pppd/fsm.h" +#include "pppd/lcp.h" + +#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 + +/* Calculate time remaining until *exp, return 0 if now >= *exp */ +static int time_left(struct timeval *diff, struct timeval *exp) +{ + struct timeval now; + + if (get_time(&now) < 0) { + error("get_time: %m"); + return 0; + } + + if (now.tv_sec > exp->tv_sec + || (now.tv_sec == exp->tv_sec && now.tv_usec >= exp->tv_usec)) + return 0; + + diff->tv_sec = exp->tv_sec - now.tv_sec; + diff->tv_usec = exp->tv_usec - now.tv_usec; + if (diff->tv_usec < 0) { + diff->tv_usec += 1000000; + --diff->tv_sec; + } + + return 1; +} + +/********************************************************************** +*%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. +***********************************************************************/ +static void +parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + PPPoETag *tag = extra; + + if (type == TAG_HOST_UNIQ && len == ntohs(tag->length)) + tag->length = memcmp(data, tag->payload, len); +} + +/********************************************************************** +*%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. +***********************************************************************/ +static int +packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) +{ + PPPoETag hostUniq = conn->hostUniq; + + /* If packet is not directed to our MAC address, forget it */ + if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; + + /* If we're not using the Host-Unique tag, then accept the packet */ + if (!conn->hostUniq.length) return 1; + + parsePacket(packet, parseForHostUniq, &hostUniq); + return !hostUniq.length; +} + +/********************************************************************** +*%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 +***********************************************************************/ +static void +parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + struct PacketCriteria *pc = (struct PacketCriteria *) extra; + PPPoEConnection *conn = pc->conn; + UINT16_t mru; + int i; + + switch(type) { + case TAG_AC_NAME: + pc->seenACName = 1; + if (conn->printACNames) { + info("Access-Concentrator: %.*s", (int) len, data); + } + if (conn->acName && len == strlen(conn->acName) && + !strncmp((char *) data, conn->acName, len)) { + pc->acNameOK = 1; + } + break; + case TAG_SERVICE_NAME: + pc->seenServiceName = 1; + if (conn->serviceName && len == strlen(conn->serviceName) && + !strncmp((char *) data, conn->serviceName, len)) { + pc->serviceNameOK = 1; + } + break; + case TAG_AC_COOKIE: + conn->cookie.type = htons(type); + conn->cookie.length = htons(len); + memcpy(conn->cookie.payload, data, len); + break; + case TAG_RELAY_SESSION_ID: + conn->relayId.type = htons(type); + conn->relayId.length = htons(len); + memcpy(conn->relayId.payload, data, len); + break; + case TAG_PPP_MAX_PAYLOAD: + if (len == sizeof(mru)) { + memcpy(&mru, data, sizeof(mru)); + mru = ntohs(mru); + if (mru >= ETH_PPPOE_MTU) { + if (lcp_allowoptions[0].mru > mru) + lcp_allowoptions[0].mru = mru; + if (lcp_wantoptions[0].mru > mru) + lcp_wantoptions[0].mru = mru; + conn->seenMaxPayload = 1; + } + } + break; + case TAG_SERVICE_NAME_ERROR: + error("PADO: Service-Name-Error: %.*s", (int) len, data); + conn->error = 1; + break; + case TAG_AC_SYSTEM_ERROR: + error("PADO: System-Error: %.*s", (int) len, data); + conn->error = 1; + break; + case TAG_GENERIC_ERROR: + error("PADO: Generic-Error: %.*s", (int) len, data); + conn->error = 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 +***********************************************************************/ +static void +parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + PPPoEConnection *conn = (PPPoEConnection *) extra; + UINT16_t mru; + switch(type) { + case TAG_SERVICE_NAME: + dbglog("PADS: Service-Name: '%.*s'", (int) len, data); + break; + case TAG_PPP_MAX_PAYLOAD: + if (len == sizeof(mru)) { + memcpy(&mru, data, sizeof(mru)); + mru = ntohs(mru); + if (mru >= ETH_PPPOE_MTU) { + if (lcp_allowoptions[0].mru > mru) + lcp_allowoptions[0].mru = mru; + if (lcp_wantoptions[0].mru > mru) + lcp_wantoptions[0].mru = mru; + conn->seenMaxPayload = 1; + } + } + break; + case TAG_SERVICE_NAME_ERROR: + error("PADS: Service-Name-Error: %.*s", (int) len, data); + conn->error = 1; + break; + case TAG_AC_SYSTEM_ERROR: + error("PADS: System-Error: %.*s", (int) len, data); + conn->error = 1; + break; + case TAG_GENERIC_ERROR: + error("PADS: Generic-Error: %.*s", (int) len, data); + conn->error = 1; + break; + 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 +***********************************************************************/ +static void +sendPADI(PPPoEConnection *conn) +{ + PPPoEPacket packet; + unsigned char *cursor = packet.payload; + PPPoETag *svc = (PPPoETag *) (&packet.payload); + UINT16_t namelen = 0; + UINT16_t plen; + int omit_service_name = 0; + + if (conn->serviceName) { + namelen = (UINT16_t) strlen(conn->serviceName); + if (!strcmp(conn->serviceName, "NO-SERVICE-NAME-NON-RFC-COMPLIANT")) { + omit_service_name = 1; + } + } + + /* 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.vertype = PPPOE_VER_TYPE(1, 1); + packet.code = CODE_PADI; + packet.session = 0; + + if (!omit_service_name) { + plen = TAG_HDR_SIZE + namelen; + CHECK_ROOM(cursor, packet.payload, plen); + + svc->type = TAG_SERVICE_NAME; + svc->length = htons(namelen); + + if (conn->serviceName) { + memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); + } + cursor += namelen + TAG_HDR_SIZE; + } else { + plen = 0; + } + + /* If we're using Host-Uniq, copy it over */ + if (conn->hostUniq.length) { + int len = ntohs(conn->hostUniq.length); + CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); + memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); + cursor += len + TAG_HDR_SIZE; + plen += len + TAG_HDR_SIZE; + } + + /* Add our maximum MTU/MRU */ + if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) { + PPPoETag maxPayload; + UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru)); + maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); + maxPayload.length = htons(sizeof(mru)); + memcpy(maxPayload.payload, &mru, sizeof(mru)); + CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE); + memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); + cursor += sizeof(mru) + TAG_HDR_SIZE; + plen += sizeof(mru) + TAG_HDR_SIZE; + } + + packet.length = htons(plen); + + sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); +} + +/********************************************************************** +*%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; + struct timeval expire_at; + + PPPoEPacket packet; + int len; + + struct PacketCriteria pc; + pc.conn = conn; + pc.acNameOK = (conn->acName) ? 0 : 1; + pc.serviceNameOK = (conn->serviceName) ? 0 : 1; + pc.seenACName = 0; + pc.seenServiceName = 0; + conn->seenMaxPayload = 0; + conn->error = 0; + + if (get_time(&expire_at) < 0) { + error("get_time (waitForPADO): %m"); + return; + } + expire_at.tv_sec += timeout; + + do { + if (BPF_BUFFER_IS_EMPTY) { + if (!time_left(&tv, &expire_at)) + return; /* Timed out */ + + FD_ZERO(&readable); + FD_SET(conn->discoverySocket, &readable); + + while(1) { + r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); + if (r >= 0 || errno != EINTR || got_sigterm) break; + } + if (r < 0) { + error("select (waitForPADO): %m"); + return; + } + if (r == 0) + return; /* Timed out */ + } + + /* Get the packet */ + receivePacket(conn->discoverySocket, &packet, &len); + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > len) { + error("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 it's not for us, loop again */ + if (!packetIsForMe(conn, &packet)) continue; + + if (packet.code == CODE_PADO) { + if (NOT_UNICAST(packet.ethHdr.h_source)) { + error("Ignoring PADO packet from non-unicast MAC address"); + continue; + } + if (conn->req_peer + && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) { + warn("Ignoring PADO packet from wrong MAC address"); + continue; + } + if (parsePacket(&packet, parsePADOTags, &pc) < 0) + return; + if (conn->error) + return; + if (!pc.seenACName) { + error("Ignoring PADO packet with no AC-Name tag"); + continue; + } + if (!pc.seenServiceName) { + error("Ignoring PADO packet with no Service-Name tag"); + continue; + } + conn->numPADOs++; + if (pc.acNameOK && pc.serviceNameOK) { + memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); + 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 +***********************************************************************/ +static 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.vertype = PPPOE_VER_TYPE(1, 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->hostUniq.length) { + int len = ntohs(conn->hostUniq.length); + CHECK_ROOM(cursor, packet.payload, len+TAG_HDR_SIZE); + memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); + cursor += len + TAG_HDR_SIZE; + plen += len + TAG_HDR_SIZE; + } + + /* Add our maximum MTU/MRU */ + if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) { + PPPoETag maxPayload; + UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru)); + maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); + maxPayload.length = htons(sizeof(mru)); + memcpy(maxPayload.payload, &mru, sizeof(mru)); + CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE); + memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); + cursor += sizeof(mru) + TAG_HDR_SIZE; + plen += sizeof(mru) + 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)); +} + +/********************************************************************** +*%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 +***********************************************************************/ +static void +waitForPADS(PPPoEConnection *conn, int timeout) +{ + fd_set readable; + int r; + struct timeval tv; + struct timeval expire_at; + + PPPoEPacket packet; + int len; + + if (get_time(&expire_at) < 0) { + error("get_time (waitForPADS): %m"); + return; + } + expire_at.tv_sec += timeout; + + conn->error = 0; + do { + if (BPF_BUFFER_IS_EMPTY) { + if (!time_left(&tv, &expire_at)) + return; /* Timed out */ + + FD_ZERO(&readable); + FD_SET(conn->discoverySocket, &readable); + + while(1) { + r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); + if (r >= 0 || errno != EINTR || got_sigterm) break; + } + if (r < 0) { + error("select (waitForPADS): %m"); + return; + } + if (r == 0) + return; /* Timed out */ + } + + /* Get the packet */ + receivePacket(conn->discoverySocket, &packet, &len); + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > len) { + error("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 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 */ + if (parsePacket(&packet, parsePADSTags, conn) < 0) + return; + if (conn->error) + return; + 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; + + 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) { + error("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 = conn->discoveryTimeout; + + do { + padiAttempts++; + if (got_sigterm || padiAttempts > conn->discoveryAttempts) { + warn("Timeout waiting for PADO packets"); + close(conn->discoverySocket); + conn->discoverySocket = -1; + return; + } + sendPADI(conn); + conn->discoveryState = STATE_SENT_PADI; + waitForPADO(conn, timeout); + + timeout *= 2; + } while (conn->discoveryState == STATE_SENT_PADI); + + timeout = conn->discoveryTimeout; + do { + padrAttempts++; + if (got_sigterm || padrAttempts > conn->discoveryAttempts) { + warn("Timeout waiting for PADS packets"); + close(conn->discoverySocket); + conn->discoverySocket = -1; + return; + } + sendPADR(conn); + conn->discoveryState = STATE_SENT_PADR; + waitForPADS(conn, timeout); + timeout *= 2; + } while (conn->discoveryState == STATE_SENT_PADR); + + if (!conn->seenMaxPayload) { + /* RFC 4638: MUST limit MTU/MRU to 1492 */ + if (lcp_allowoptions[0].mru > ETH_PPPOE_MTU) + lcp_allowoptions[0].mru = ETH_PPPOE_MTU; + if (lcp_wantoptions[0].mru > ETH_PPPOE_MTU) + lcp_wantoptions[0].mru = ETH_PPPOE_MTU; + } + + /* We're done. */ + close(conn->discoverySocket); + conn->discoverySocket = -1; + conn->discoveryState = STATE_SESSION; + return; +} diff --git a/pppd/plugins/pppoe/if.c b/pppd/plugins/pppoe/if.c new file mode 100644 index 0000000..225dd56 --- /dev/null +++ b/pppd/plugins/pppoe/if.c @@ -0,0 +1,242 @@ +/*********************************************************************** +* +* if.c +* +* Implementation of user-space PPPoE redirector for Linux. +* +* Functions for opening a raw socket and reading/writing raw Ethernet frames. +* +* 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: if.c,v 1.2 2008/06/09 08:34:23 paulus Exp $"; + +#define _GNU_SOURCE 1 +#include "pppoe.h" +#include "pppd/pppd.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +#include +#elif defined(HAVE_LINUX_IF_PACKET_H) +#include +#endif + +#ifdef HAVE_ASM_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_NET_IF_ARP_H +#include +#endif + +/* Initialize frame types to RFC 2516 values. Some broken peers apparently + use different frame types... sigh... */ + +UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; +UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; + +/********************************************************************** +*%FUNCTION: etherType +*%ARGUMENTS: +* packet -- a received PPPoE packet +*%RETURNS: +* ethernet packet type (see /usr/include/net/ethertypes.h) +*%DESCRIPTION: +* Checks the ethernet packet header to determine its type. +* We should only be receveing DISCOVERY and SESSION types if the BPF +* is set up correctly. Logs an error if an unexpected type is received. +* Note that the ethernet type names come from "pppoe.h" and the packet +* packet structure names use the LINUX dialect to maintain consistency +* with the rest of this file. See the BSD section of "pppoe.h" for +* translations of the data structure names. +***********************************************************************/ +UINT16_t +etherType(PPPoEPacket *packet) +{ + UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); + if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { + error("Invalid ether type 0x%x", type); + } + return type; +} + +/********************************************************************** +*%FUNCTION: openInterface +*%ARGUMENTS: +* ifname -- name of interface +* type -- Ethernet frame type +* hwaddr -- if non-NULL, set to the hardware address +*%RETURNS: +* A raw socket for talking to the Ethernet card. Exits on error. +*%DESCRIPTION: +* Opens a raw Ethernet socket +***********************************************************************/ +int +openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) +{ + int optval=1; + int fd; + struct ifreq ifr; + int domain, stype; + +#ifdef HAVE_STRUCT_SOCKADDR_LL + struct sockaddr_ll sa; +#else + struct sockaddr sa; +#endif + + memset(&sa, 0, sizeof(sa)); + +#ifdef HAVE_STRUCT_SOCKADDR_LL + domain = PF_PACKET; + stype = SOCK_RAW; +#else + domain = PF_INET; + stype = SOCK_PACKET; +#endif + + if ((fd = socket(domain, stype, htons(type))) < 0) { + /* Give a more helpful message for the common error case */ + if (errno == EPERM) { + fatal("Cannot create raw socket -- pppoe must be run as root."); + } + error("Can't open socket for pppoe: %m"); + return -1; + } + + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { + error("Can't set socket options for pppoe: %m"); + close(fd); + return -1; + } + + /* Fill in hardware address */ + if (hwaddr) { + strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + error("Can't get hardware address for %s: %m", ifname); + close(fd); + return -1; + } + memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); +#ifdef ARPHRD_ETHER + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + warn("Interface %.16s is not Ethernet", ifname); + } +#endif + if (NOT_UNICAST(hwaddr)) { + fatal("Can't use interface %.16s: it has broadcast/multicast MAC address", + ifname); + } + } + + /* Sanity check on MTU */ + strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { + error("Can't get MTU for %s: %m", ifname); + } else if (ifr.ifr_mtu < ETH_DATA_LEN) { + error("Interface %.16s has MTU of %d -- should be at least %d.", + ifname, ifr.ifr_mtu, ETH_DATA_LEN); + error("This may cause serious connection problems."); + } + +#ifdef HAVE_STRUCT_SOCKADDR_LL + /* Get interface index */ + sa.sll_family = AF_PACKET; + sa.sll_protocol = htons(type); + + strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { + error("Could not get interface index for %s: %m", ifname); + close(fd); + return -1; + } + sa.sll_ifindex = ifr.ifr_ifindex; + +#else + strcpy(sa.sa_data, ifname); +#endif + + /* We're only interested in packets on specified interface */ + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + error("Failed to bind to interface %s: %m", ifname); + close(fd); + return -1; + } + + return fd; +} + + +/*********************************************************************** +*%FUNCTION: sendPacket +*%ARGUMENTS: +* sock -- socket to send to +* pkt -- the packet to transmit +* size -- size of packet (in bytes) +*%RETURNS: +* 0 on success; -1 on failure +*%DESCRIPTION: +* Transmits a packet +***********************************************************************/ +int +sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) +{ + int err; + + if (debug) + pppoe_log_packet("Send ", pkt); +#if defined(HAVE_STRUCT_SOCKADDR_LL) + err = send(sock, pkt, size, 0); +#else + struct sockaddr sa; + + strcpy(sa.sa_data, conn->ifName); + err = sendto(sock, pkt, size, 0, &sa, sizeof(sa)); +#endif + if (err < 0) { + error("error sending pppoe packet: %m"); + return -1; + } + return 0; +} + +/*********************************************************************** +*%FUNCTION: receivePacket +*%ARGUMENTS: +* sock -- socket to read from +* pkt -- place to store the received packet +* size -- set to size of packet in bytes +*%RETURNS: +* >= 0 if all OK; < 0 if error +*%DESCRIPTION: +* Receives a packet +***********************************************************************/ +int +receivePacket(int sock, PPPoEPacket *pkt, int *size) +{ + if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { + error("error receiving pppoe packet: %m"); + return -1; + } + if (debug) + pppoe_log_packet("Recv ", pkt); + return 0; +} diff --git a/pppd/plugins/pppoe/plugin.c b/pppd/plugins/pppoe/plugin.c new file mode 100644 index 0000000..f705b5f --- /dev/null +++ b/pppd/plugins/pppoe/plugin.c @@ -0,0 +1,472 @@ +/*********************************************************************** +* +* plugin.c +* +* pppd plugin for kernel-mode PPPoE on Linux +* +* Copyright (C) 2001 by Roaring Penguin Software Inc., Michal Ostrowski +* and Jamal Hadi Salim. +* +* Much code and many ideas derived from pppoe plugin by Michal +* Ostrowski and Jamal Hadi Salim, which carries this copyright: +* +* Copyright 2000 Michal Ostrowski , +* Jamal Hadi Salim +* Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr., +* which is based in part on work from Jens Axboe and Paul Mackerras. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version +* 2 of the License, or (at your option) any later version. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id: plugin.c,v 1.17 2008/06/15 04:35:50 paulus Exp $"; + +#define _GNU_SOURCE 1 +#include "pppoe.h" + +#include "pppd/pppd.h" +#include "pppd/fsm.h" +#include "pppd/lcp.h" +#include "pppd/ipcp.h" +#include "pppd/ccp.h" +/* #include "pppd/pathnames.h" */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _ROOT_PATH +#define _ROOT_PATH "" +#endif + +#define _PATH_ETHOPT _ROOT_PATH "/etc/ppp/options." + +char pppd_version[] = VERSION; + +/* From sys-linux.c in pppd -- MUST FIX THIS! */ +extern int new_style_driver; + +char *pppd_pppoe_service = NULL; +static char *acName = NULL; +static char *existingSession = NULL; +static int printACNames = 0; +static char *pppoe_reqd_mac = NULL; +unsigned char pppoe_reqd_mac_addr[6]; +static char *pppoe_host_uniq; +static int pppoe_padi_timeout = PADI_TIMEOUT; +static int pppoe_padi_attempts = MAX_PADI_ATTEMPTS; + +static int PPPoEDevnameHook(char *cmd, char **argv, int doit); +static option_t Options[] = { + { "device name", o_wild, (void *) &PPPoEDevnameHook, + "PPPoE device name", + OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, + devnam}, + { "pppoe-service", o_string, &pppd_pppoe_service, + "Desired PPPoE service name" }, + { "rp_pppoe_service", o_string, &pppd_pppoe_service, + "Legacy alias for pppoe-service", OPT_ALIAS }, + { "pppoe-ac", o_string, &acName, + "Desired PPPoE access concentrator name" }, + { "rp_pppoe_ac", o_string, &acName, + "Legacy alias for pppoe-ac", OPT_ALIAS }, + { "pppoe-sess", o_string, &existingSession, + "Attach to existing session (sessid:macaddr)" }, + { "rp_pppoe_sess", o_string, &existingSession, + "Legacy alias for pppoe-sess", OPT_ALIAS }, + { "pppoe-verbose", o_int, &printACNames, + "Be verbose about discovered access concentrators" }, + { "rp_pppoe_verbose", o_int, &printACNames, + "Legacy alias for pppoe-verbose", OPT_ALIAS }, + { "pppoe-mac", o_string, &pppoe_reqd_mac, + "Only connect to specified MAC address" }, + { "pppoe-host-uniq", o_string, &pppoe_host_uniq, + "Set the Host-Uniq to the supplied hex string" }, + { "host-uniq", o_string, &pppoe_host_uniq, + "Legacy alias for pppoe-host-uniq", OPT_ALIAS }, + { "pppoe-padi-timeout", o_int, &pppoe_padi_timeout, + "Initial timeout for discovery packets in seconds" }, + { "pppoe-padi-attempts", o_int, &pppoe_padi_attempts, + "Number of discovery attempts" }, + { NULL } +}; +int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL; +static PPPoEConnection *conn = NULL; + +/********************************************************************** + * %FUNCTION: PPPOEInitDevice + * %ARGUMENTS: + * None + * %RETURNS: + * + * %DESCRIPTION: + * Initializes PPPoE device. + ***********************************************************************/ +static int +PPPOEInitDevice(void) +{ + conn = malloc(sizeof(PPPoEConnection)); + if (!conn) { + novm("PPPoE session data"); + } + memset(conn, 0, sizeof(PPPoEConnection)); + conn->ifName = devnam; + conn->discoverySocket = -1; + conn->sessionSocket = -1; + conn->printACNames = printACNames; + conn->discoveryTimeout = pppoe_padi_timeout; + conn->discoveryAttempts = pppoe_padi_attempts; + return 1; +} + +/********************************************************************** + * %FUNCTION: PPPOEConnectDevice + * %ARGUMENTS: + * None + * %RETURNS: + * Non-negative if all goes well; -1 otherwise + * %DESCRIPTION: + * Connects PPPoE device. + ***********************************************************************/ +static int +PPPOEConnectDevice(void) +{ + struct sockaddr_pppox sp; + struct ifreq ifr; + int s; + + /* Open session socket before discovery phase, to avoid losing session */ + /* packets sent by peer just after PADS packet (noted on some Cisco */ + /* server equipment). */ + /* Opening this socket just before waitForPADS in the discovery() */ + /* function would be more appropriate, but it would mess-up the code */ + conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE); + if (conn->sessionSocket < 0) { + error("Failed to create PPPoE socket: %m"); + return -1; + } + + /* Restore configuration */ + lcp_allowoptions[0].mru = conn->mtu; + lcp_wantoptions[0].mru = conn->mru; + + /* Update maximum MRU */ + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + error("Can't get MTU for %s: %m", conn->ifName); + goto errout; + } + strlcpy(ifr.ifr_name, conn->ifName, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFMTU, &ifr) < 0) { + error("Can't get MTU for %s: %m", conn->ifName); + close(s); + goto errout; + } + close(s); + + if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) + lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; + if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) + lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; + + if (pppoe_host_uniq) { + if (!parseHostUniq(pppoe_host_uniq, &conn->hostUniq)) + fatal("Illegal value for pppoe-host-uniq option"); + } else { + /* if a custom host-uniq is not supplied, use our PID */ + pid_t pid = getpid(); + conn->hostUniq.type = htons(TAG_HOST_UNIQ); + conn->hostUniq.length = htons(sizeof(pid)); + memcpy(conn->hostUniq.payload, &pid, sizeof(pid)); + } + + conn->acName = acName; + conn->serviceName = pppd_pppoe_service; + strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); + if (existingSession) { + unsigned int mac[ETH_ALEN]; + int i, ses; + if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x", + &ses, &mac[0], &mac[1], &mac[2], + &mac[3], &mac[4], &mac[5]) != 7) { + fatal("Illegal value for pppoe-sess option"); + } + conn->session = htons(ses); + for (i=0; ipeerEth[i] = (unsigned char) mac[i]; + } + } else { + conn->discoverySocket = + openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); + discovery(conn); + if (conn->discoveryState != STATE_SESSION) { + error("Unable to complete PPPoE Discovery"); + goto errout; + } + } + + /* Set PPPoE session-number for further consumption */ + ppp_session_number = ntohs(conn->session); + + sp.sa_family = AF_PPPOX; + sp.sa_protocol = PX_PROTO_OE; + sp.sa_addr.pppoe.sid = conn->session; + memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); + memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); + + /* Set remote_number for ServPoET */ + sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X", + (unsigned) conn->peerEth[0], + (unsigned) conn->peerEth[1], + (unsigned) conn->peerEth[2], + (unsigned) conn->peerEth[3], + (unsigned) conn->peerEth[4], + (unsigned) conn->peerEth[5]); + + warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s", + (unsigned) conn->peerEth[0], + (unsigned) conn->peerEth[1], + (unsigned) conn->peerEth[2], + (unsigned) conn->peerEth[3], + (unsigned) conn->peerEth[4], + (unsigned) conn->peerEth[5], + conn->ifName); + + script_setenv("MACREMOTE", remote_number, 0); + + if (connect(conn->sessionSocket, (struct sockaddr *) &sp, + sizeof(struct sockaddr_pppox)) < 0) { + error("Failed to connect PPPoE socket: %d %m", errno); + goto errout; + } + + return conn->sessionSocket; + + errout: + if (conn->discoverySocket >= 0) { + sendPADT(conn, NULL); + close(conn->discoverySocket); + conn->discoverySocket = -1; + } + close(conn->sessionSocket); + return -1; +} + +static void +PPPOERecvConfig(int mru, + u_int32_t asyncmap, + int pcomp, + int accomp) +{ +#if 0 /* broken protocol, but no point harrassing the users I guess... */ + if (mru > MAX_PPPOE_MTU) + warn("Couldn't increase MRU to %d", mru); +#endif +} + +/********************************************************************** + * %FUNCTION: PPPOEDisconnectDevice + * %ARGUMENTS: + * None + * %RETURNS: + * Nothing + * %DESCRIPTION: + * Disconnects PPPoE device + ***********************************************************************/ +static void +PPPOEDisconnectDevice(void) +{ + struct sockaddr_pppox sp; + + sp.sa_family = AF_PPPOX; + sp.sa_protocol = PX_PROTO_OE; + sp.sa_addr.pppoe.sid = 0; + memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); + memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); + if (connect(conn->sessionSocket, (struct sockaddr *) &sp, + sizeof(struct sockaddr_pppox)) < 0 && errno != EALREADY) + error("Failed to disconnect PPPoE socket: %d %m", errno); + close(conn->sessionSocket); + if (conn->discoverySocket >= 0) { + sendPADT(conn, NULL); + close(conn->discoverySocket); + } +} + +static void +PPPOEDeviceOptions(void) +{ + char buf[MAXPATHLEN]; + + strlcpy(buf, _PATH_ETHOPT, MAXPATHLEN); + strlcat(buf, devnam, MAXPATHLEN); + if (!options_from_file(buf, 0, 0, 1)) + exit(EXIT_OPTION_ERROR); + +} + +struct channel pppoe_channel; + +/********************************************************************** + * %FUNCTION: PPPoEDevnameHook + * %ARGUMENTS: + * cmd -- the command (actually, the device name + * argv -- argument vector + * doit -- if non-zero, set device name. Otherwise, just check if possible + * %RETURNS: + * 1 if we will handle this device; 0 otherwise. + * %DESCRIPTION: + * Checks if name is a valid interface name; if so, returns 1. Also + * sets up devnam (string representation of device). + ***********************************************************************/ +static int +PPPoEDevnameHook(char *cmd, char **argv, int doit) +{ + int r = 1; + int fd; + struct ifreq ifr; + + /* + * Take any otherwise-unrecognized option as a possible device name, + * and test if it is the name of a network interface with a + * hardware address whose sa_family is ARPHRD_ETHER. + */ + if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) { + /* Strip off "nic-" */ + cmd += 4; + } + + /* Open a socket */ + if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { + r = 0; + } + + /* Try getting interface index */ + if (r) { + strlcpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { + r = 0; + } else { + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + r = 0; + } else { + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + if (doit) + error("Interface %s not Ethernet", cmd); + r = 0; + } + } + } + } + + /* Close socket */ + close(fd); + if (r && doit) { + strlcpy(devnam, cmd, sizeof(devnam)); + if (the_channel != &pppoe_channel) { + + the_channel = &pppoe_channel; + modem = 0; + + PPPOEInitDevice(); + } + return 1; + } + + return r; +} + +/********************************************************************** + * %FUNCTION: plugin_init + * %ARGUMENTS: + * None + * %RETURNS: + * Nothing + * %DESCRIPTION: + * Initializes hooks for pppd plugin + ***********************************************************************/ +void +plugin_init(void) +{ + if (!ppp_available() && !new_style_driver) { + fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?"); + } + + add_options(Options); + + info("PPPoE plugin from pppd %s", VERSION); +} + +void pppoe_check_options(void) +{ + unsigned int mac[6]; + int i; + + if (pppoe_reqd_mac != NULL) { + if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x", + &mac[0], &mac[1], &mac[2], &mac[3], + &mac[4], &mac[5]) != 6) { + option_error("cannot parse pppoe-mac option value"); + exit(EXIT_OPTION_ERROR); + } + for (i = 0; i < 6; ++i) + conn->req_peer_mac[i] = mac[i]; + conn->req_peer = 1; + } + + lcp_allowoptions[0].neg_accompression = 0; + lcp_wantoptions[0].neg_accompression = 0; + + lcp_allowoptions[0].neg_asyncmap = 0; + lcp_wantoptions[0].neg_asyncmap = 0; + + lcp_allowoptions[0].neg_pcompression = 0; + lcp_wantoptions[0].neg_pcompression = 0; + + if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU) + lcp_allowoptions[0].mru = MAX_PPPOE_MTU; + if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU) + lcp_wantoptions[0].mru = MAX_PPPOE_MTU; + + /* Save configuration */ + conn->mtu = lcp_allowoptions[0].mru; + conn->mru = lcp_wantoptions[0].mru; + + ccp_allowoptions[0].deflate = 0; + ccp_wantoptions[0].deflate = 0; + + ipcp_allowoptions[0].neg_vj = 0; + ipcp_wantoptions[0].neg_vj = 0; + + ccp_allowoptions[0].bsd_compress = 0; + ccp_wantoptions[0].bsd_compress = 0; +} + +struct channel pppoe_channel = { + .options = Options, + .process_extra_options = &PPPOEDeviceOptions, + .check_options = pppoe_check_options, + .connect = &PPPOEConnectDevice, + .disconnect = &PPPOEDisconnectDevice, + .establish_ppp = &generic_establish_ppp, + .disestablish_ppp = &generic_disestablish_ppp, + .send_config = NULL, + .recv_config = &PPPOERecvConfig, + .close = NULL, + .cleanup = NULL +}; diff --git a/pppd/plugins/pppoe/pppoe-discovery.c b/pppd/plugins/pppoe/pppoe-discovery.c new file mode 100644 index 0000000..e41d286 --- /dev/null +++ b/pppd/plugins/pppoe/pppoe-discovery.c @@ -0,0 +1,784 @@ +/* + * Perform PPPoE discovery + * + * Copyright (C) 2000-2001 by Roaring Penguin Software Inc. + * Copyright (C) 2004 Marco d'Itri + * + * This program may be distributed according to the terms of the GNU + * General Public License, version 2 or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pppoe.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +#include +#elif defined(HAVE_LINUX_IF_PACKET_H) +#include +#endif + +#ifdef HAVE_ASM_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_NET_IF_ARP_H +#include +#endif + +char *xstrdup(const char *s); +void usage(void); + +void die(int status) +{ + exit(status); +} + +void error(char *fmt, ...) +{ + va_list pvar; + va_start(pvar, fmt); + vfprintf(stderr, fmt, pvar); + va_end(pvar); +} + +/* Initialize frame types to RFC 2516 values. Some broken peers apparently + use different frame types... sigh... */ + +UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; +UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; + +/********************************************************************** +*%FUNCTION: etherType +*%ARGUMENTS: +* packet -- a received PPPoE packet +*%RETURNS: +* ethernet packet type (see /usr/include/net/ethertypes.h) +*%DESCRIPTION: +* Checks the ethernet packet header to determine its type. +* We should only be receveing DISCOVERY and SESSION types if the BPF +* is set up correctly. Logs an error if an unexpected type is received. +* Note that the ethernet type names come from "pppoe.h" and the packet +* packet structure names use the LINUX dialect to maintain consistency +* with the rest of this file. See the BSD section of "pppoe.h" for +* translations of the data structure names. +***********************************************************************/ +UINT16_t +etherType(PPPoEPacket *packet) +{ + UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); + if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { + fprintf(stderr, "Invalid ether type 0x%x\n", type); + } + return type; +} + +/********************************************************************** +*%FUNCTION: openInterface +*%ARGUMENTS: +* ifname -- name of interface +* type -- Ethernet frame type +* hwaddr -- if non-NULL, set to the hardware address +*%RETURNS: +* A raw socket for talking to the Ethernet card. Exits on error. +*%DESCRIPTION: +* Opens a raw Ethernet socket +***********************************************************************/ +int +openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) +{ + int optval=1; + int fd; + struct ifreq ifr; + int domain, stype; + +#ifdef HAVE_STRUCT_SOCKADDR_LL + struct sockaddr_ll sa; +#else + struct sockaddr sa; +#endif + + memset(&sa, 0, sizeof(sa)); + +#ifdef HAVE_STRUCT_SOCKADDR_LL + domain = PF_PACKET; + stype = SOCK_RAW; +#else + domain = PF_INET; + stype = SOCK_PACKET; +#endif + + if ((fd = socket(domain, stype, htons(type))) < 0) { + /* Give a more helpful message for the common error case */ + if (errno == EPERM) { + fatal("Cannot create raw socket -- pppoe must be run as root."); + } + fatalSys("socket"); + } + + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { + fatalSys("setsockopt"); + } + + /* Fill in hardware address */ + if (hwaddr) { + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + fatalSys("ioctl(SIOCGIFHWADDR)"); + } + memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); +#ifdef ARPHRD_ETHER + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + fatal("Interface %.16s is not Ethernet", ifname); + } +#endif + if (NOT_UNICAST(hwaddr)) { + fatal("Interface %.16s has broadcast/multicast MAC address??", ifname); + } + } + + /* Sanity check on MTU */ + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { + fatalSys("ioctl(SIOCGIFMTU)"); + } + if (ifr.ifr_mtu < ETH_DATA_LEN) { + fprintf(stderr, "Interface %.16s has MTU of %d -- should be %d.\n", + ifname, ifr.ifr_mtu, ETH_DATA_LEN); + fprintf(stderr, "You may have serious connection problems.\n"); + } + +#ifdef HAVE_STRUCT_SOCKADDR_LL + /* Get interface index */ + sa.sll_family = AF_PACKET; + sa.sll_protocol = htons(type); + + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ - 1] = 0; + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { + fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index"); + } + sa.sll_ifindex = ifr.ifr_ifindex; + +#else + strcpy(sa.sa_data, ifname); +#endif + + /* We're only interested in packets on specified interface */ + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + fatalSys("bind"); + } + + return fd; +} + + +/*********************************************************************** +*%FUNCTION: sendPacket +*%ARGUMENTS: +* sock -- socket to send to +* pkt -- the packet to transmit +* size -- size of packet (in bytes) +*%RETURNS: +* 0 on success; -1 on failure +*%DESCRIPTION: +* Transmits a packet +***********************************************************************/ +int +sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) +{ +#if defined(HAVE_STRUCT_SOCKADDR_LL) + if (send(sock, pkt, size, 0) < 0) { + fatalSys("send (sendPacket)"); + return -1; + } +#else + struct sockaddr sa; + + if (!conn) { + fatal("relay and server not supported on Linux 2.0 kernels"); + } + strcpy(sa.sa_data, conn->ifName); + if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) { + fatalSys("sendto (sendPacket)"); + return -1; + } +#endif + return 0; +} + +/*********************************************************************** +*%FUNCTION: receivePacket +*%ARGUMENTS: +* sock -- socket to read from +* pkt -- place to store the received packet +* size -- set to size of packet in bytes +*%RETURNS: +* >= 0 if all OK; < 0 if error +*%DESCRIPTION: +* Receives a packet +***********************************************************************/ +int +receivePacket(int sock, PPPoEPacket *pkt, int *size) +{ + if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { + fatalSys("recv (receivePacket)"); + return -1; + } + return 0; +} + +/********************************************************************** +*%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 (PPPOE_VER(packet->vertype) != 1) { + fprintf(stderr, "Invalid PPPoE version (%d)\n", + PPPOE_VER(packet->vertype)); + return -1; + } + if (PPPOE_TYPE(packet->vertype) != 1) { + fprintf(stderr, "Invalid PPPoE type (%d)\n", + PPPOE_TYPE(packet->vertype)); + return -1; + } + + /* Do some sanity checks on packet */ + if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */ + fprintf(stderr, "Invalid PPPoE packet length (%u)\n", 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 = (curTag[0] << 8) + curTag[1]; + tagLen = (curTag[2] << 8) + curTag[3]; + if (tagType == TAG_END_OF_LIST) { + return 0; + } + if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { + fprintf(stderr, "Invalid PPPoE tag length (%u)\n", tagLen); + return -1; + } + func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); + curTag = curTag + TAG_HDR_SIZE + tagLen; + } + return 0; +} + +/********************************************************************** +*%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) +{ + PPPoETag *tag = extra; + + if (type == TAG_HOST_UNIQ && len == ntohs(tag->length)) + tag->length = memcmp(data, tag->payload, len); +} + +/********************************************************************** +*%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) +{ + PPPoETag hostUniq = conn->hostUniq; + + /* If packet is not directed to our MAC address, forget it */ + if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; + + /* If we're not using the Host-Unique tag, then accept the packet */ + if (!conn->hostUniq.length) return 1; + + parsePacket(packet, parseForHostUniq, &hostUniq); + return !hostUniq.length; +} + +/********************************************************************** +*%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: + pc->seenACName = 1; + 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: + pc->seenServiceName = 1; + 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); + } + break; + case TAG_AC_SYSTEM_ERROR: + if (conn->printACNames) { + printf("Got a System-Error tag: %.*s\n", (int) len, data); + } + break; + case TAG_GENERIC_ERROR: + if (conn->printACNames) { + printf("Got a Generic-Error tag: %.*s\n", (int) len, data); + } + 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.vertype = PPPOE_VER_TYPE(1, 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->hostUniq.length) { + int len = ntohs(conn->hostUniq.length); + CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); + memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); + cursor += len + TAG_HDR_SIZE; + plen += len + 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; + pc.seenACName = 0; + pc.seenServiceName = 0; + conn->error = 0; + + 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) { + perror("select (waitForPADO)"); + return; + } + if (r == 0) return; /* Timed out */ + } + + /* Get the packet */ + receivePacket(conn->discoverySocket, &packet, &len); + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > len) { + fprintf(stderr, "Bogus PPPoE length field (%u)\n", + (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 (BROADCAST(packet.ethHdr.h_source)) { + fprintf(stderr, "Ignoring PADO packet from broadcast MAC address\n"); + continue; + } + parsePacket(&packet, parsePADOTags, &pc); + if (conn->error) + return; + if (!pc.seenACName) { + fprintf(stderr, "Ignoring PADO packet with no AC-Name tag\n"); + continue; + } + if (!pc.seenServiceName) { + fprintf(stderr, "Ignoring PADO packet with no Service-Name tag\n"); + continue; + } + conn->numPADOs++; + 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]); + printf("--------------------------------------------------\n"); + continue; + } + conn->discoveryState = STATE_RECEIVED_PADO; + break; + } + } + } while (conn->discoveryState != STATE_RECEIVED_PADO); +} + +/********************************************************************** +*%FUNCTION: discovery +*%ARGUMENTS: +* conn -- PPPoE connection info structure +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Performs the PPPoE discovery phase +***********************************************************************/ +void +discovery(PPPoEConnection *conn) +{ + int padiAttempts = 0; + int timeout = conn->discoveryTimeout; + + conn->discoverySocket = + openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); + + do { + padiAttempts++; + if (padiAttempts > conn->discoveryAttempts) { + fprintf(stderr, "Timeout waiting for PADO packets\n"); + close(conn->discoverySocket); + conn->discoverySocket = -1; + return; + } + sendPADI(conn); + conn->discoveryState = STATE_SENT_PADI; + waitForPADO(conn, timeout); + } while (!conn->numPADOs); +} + +int main(int argc, char *argv[]) +{ + int opt; + PPPoEConnection *conn; + + conn = malloc(sizeof(PPPoEConnection)); + if (!conn) + fatalSys("malloc"); + + memset(conn, 0, sizeof(PPPoEConnection)); + + conn->printACNames = 1; + conn->discoveryTimeout = PADI_TIMEOUT; + conn->discoveryAttempts = MAX_PADI_ATTEMPTS; + + while ((opt = getopt(argc, argv, "I:D:VUQS:C:W:t:a:h")) > 0) { + switch(opt) { + case 'S': + conn->serviceName = xstrdup(optarg); + break; + case 'C': + conn->acName = xstrdup(optarg); + break; + case 't': + if (sscanf(optarg, "%d", &conn->discoveryTimeout) != 1) { + fprintf(stderr, "Illegal argument to -t: Should be -t timeout\n"); + exit(EXIT_FAILURE); + } + if (conn->discoveryTimeout < 1) { + conn->discoveryTimeout = 1; + } + break; + case 'a': + if (sscanf(optarg, "%d", &conn->discoveryAttempts) != 1) { + fprintf(stderr, "Illegal argument to -a: Should be -a attempts\n"); + exit(EXIT_FAILURE); + } + if (conn->discoveryAttempts < 1) { + conn->discoveryAttempts = 1; + } + break; + case 'U': + if(conn->hostUniq.length) { + fprintf(stderr, "-U and -W are mutually exclusive\n"); + exit(EXIT_FAILURE); + } else { + pid_t pid = getpid(); + conn->hostUniq.type = htons(TAG_HOST_UNIQ); + conn->hostUniq.length = htons(sizeof(pid)); + memcpy(conn->hostUniq.payload, &pid, sizeof(pid)); + } + break; + case 'W': + if(conn->hostUniq.length) { + fprintf(stderr, "-U and -W are mutually exclusive\n"); + exit(EXIT_FAILURE); + } + if (!parseHostUniq(optarg, &conn->hostUniq)) { + fprintf(stderr, "Invalid host-uniq argument: %s\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 'D': + conn->debugFile = fopen(optarg, "w"); + if (!conn->debugFile) { + fprintf(stderr, "Could not open %s: %s\n", + optarg, strerror(errno)); + exit(1); + } + fprintf(conn->debugFile, "pppoe-discovery from pppd %s\n", VERSION); + break; + case 'I': + conn->ifName = xstrdup(optarg); + break; + case 'Q': + conn->printACNames = 0; + break; + case 'V': + case 'h': + usage(); + exit(0); + default: + usage(); + exit(1); + } + } + + /* default interface name */ + if (!conn->ifName) + conn->ifName = strdup("eth0"); + + conn->discoverySocket = -1; + conn->sessionSocket = -1; + + discovery(conn); + + if (!conn->numPADOs) + exit(1); + else + exit(0); +} + +void fatal(char * fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + exit(1); +} + +void fatalSys(char const *str) +{ + perror(str); + exit(1); +} + +char *xstrdup(const char *s) +{ + register char *ret = strdup(s); + if (!ret) + fatalSys("strdup"); + return ret; +} + +void usage(void) +{ + fprintf(stderr, "Usage: pppoe-discovery [options]\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -I if_name -- Specify interface (default eth0)\n"); + fprintf(stderr, " -D filename -- Log debugging information in filename.\n"); + fprintf(stderr, + " -t timeout -- Initial timeout for discovery packets in seconds\n" + " -a attempts -- Number of discovery attempts\n" + " -V -- Print version and exit.\n" + " -Q -- Quit Mode: Do not print access concentrator names\n" + " -S name -- Set desired service name.\n" + " -C name -- Set desired access concentrator name.\n" + " -U -- Use Host-Unique to allow multiple PPPoE sessions.\n" + " -W hexvalue -- Set the Host-Unique to the supplied hex string.\n" + " -h -- Print usage information.\n"); + fprintf(stderr, "\npppoe-discovery from pppd " VERSION "\n"); +} diff --git a/pppd/plugins/pppoe/pppoe.h b/pppd/plugins/pppoe/pppoe.h new file mode 100644 index 0000000..4e19720 --- /dev/null +++ b/pppd/plugins/pppoe/pppoe.h @@ -0,0 +1,328 @@ +/*********************************************************************** +* +* pppoe.h +* +* Declaration of various PPPoE constants +* +* Copyright (C) 2000 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. +* +* $Id: pppoe.h,v 1.4 2008/06/15 04:35:50 paulus Exp $ +* +***********************************************************************/ + +#include "config.h" + +#include /* For FILE */ +#include /* For pid_t */ +#include +#include + +#include "pppd/pppd.h" /* For error */ + +/* How do we access raw Ethernet devices? */ +#undef USE_LINUX_PACKET +#undef USE_BPF + +#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) +#define USE_LINUX_PACKET 1 +#elif defined(HAVE_SYS_DLPI_H) +#define USE_DLPI +#elif defined(HAVE_NET_BPF_H) +#define USE_BPF 1 +#endif + +/* Sanity check */ +#if !defined(USE_BPF) && !defined(USE_LINUX_PACKET) && !defined(USE_DLPI) +#error Unknown method for accessing raw Ethernet frames +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +/* This has to be included before Linux 4.8's linux/in.h + * gets dragged in. */ +#include + +/* Ugly header files on some Linux boxes... */ +#if defined(HAVE_LINUX_IF_H) +#include +#elif defined(HAVE_NET_IF_H) +#include +#endif + +#ifdef HAVE_NET_IF_TYPES_H +#include +#endif + +#define BPF_BUFFER_IS_EMPTY 1 +#define BPF_BUFFER_HAS_DATA 0 + +/* Define various integer types -- assumes a char is 8 bits */ +#if SIZEOF_UNSIGNED_SHORT == 2 +typedef unsigned short UINT16_t; +#elif SIZEOF_UNSIGNED_INT == 2 +typedef unsigned int UINT16_t; +#else +#error Could not find a 16-bit integer type +#endif + +#if SIZEOF_UNSIGNED_SHORT == 4 +typedef unsigned short UINT32_t; +#elif SIZEOF_UNSIGNED_INT == 4 +typedef unsigned int UINT32_t; +#elif SIZEOF_UNSIGNED_LONG == 4 +typedef unsigned long UINT32_t; +#else +#error Could not find a 32-bit integer type +#endif + +#ifdef HAVE_LINUX_IF_ETHER_H +#include +#else + +#ifdef HAVE_NETINET_IF_ETHER_H +#include + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifndef HAVE_SYS_DLPI_H +#include +#endif +#endif +#endif + +/* Ethernet frame types according to RFC 2516 */ +#define ETH_PPPOE_DISCOVERY 0x8863 +#define ETH_PPPOE_SESSION 0x8864 + +/* But some brain-dead peers disobey the RFC, so frame types are variables */ +extern UINT16_t Eth_PPPOE_Discovery; +extern UINT16_t Eth_PPPOE_Session; + +/* PPPoE codes */ +#define CODE_PADI 0x09 +#define CODE_PADO 0x07 +#define CODE_PADR 0x19 +#define CODE_PADS 0x65 +#define CODE_PADT 0xA7 + +/* Extensions from draft-carrel-info-pppoe-ext-00 */ +/* I do NOT like PADM or PADN, but they are here for completeness */ +#define CODE_PADM 0xD3 +#define CODE_PADN 0xD4 + +#define CODE_SESS 0x00 + +/* PPPoE Tags */ +#define TAG_END_OF_LIST 0x0000 +#define TAG_SERVICE_NAME 0x0101 +#define TAG_AC_NAME 0x0102 +#define TAG_HOST_UNIQ 0x0103 +#define TAG_AC_COOKIE 0x0104 +#define TAG_VENDOR_SPECIFIC 0x0105 +#define TAG_RELAY_SESSION_ID 0x0110 +#define TAG_PPP_MAX_PAYLOAD 0x0120 +#define TAG_SERVICE_NAME_ERROR 0x0201 +#define TAG_AC_SYSTEM_ERROR 0x0202 +#define TAG_GENERIC_ERROR 0x0203 + +/* Extensions from draft-carrel-info-pppoe-ext-00 */ +/* I do NOT like these tags one little bit */ +#define TAG_HURL 0x111 +#define TAG_MOTM 0x112 +#define TAG_IP_ROUTE_ADD 0x121 + +/* Discovery phase states */ +#define STATE_SENT_PADI 0 +#define STATE_RECEIVED_PADO 1 +#define STATE_SENT_PADR 2 +#define STATE_SESSION 3 +#define STATE_TERMINATED 4 + +/* How many PADI/PADS attempts? */ +#define MAX_PADI_ATTEMPTS 3 + +/* Initial timeout for PADO/PADS */ +#define PADI_TIMEOUT 5 + +/* States for scanning PPP frames */ +#define STATE_WAITFOR_FRAME_ADDR 0 +#define STATE_DROP_PROTO 1 +#define STATE_BUILDING_PACKET 2 + +/* Special PPP frame characters */ +#define FRAME_ESC 0x7D +#define FRAME_FLAG 0x7E +#define FRAME_ADDR 0xFF +#define FRAME_CTRL 0x03 +#define FRAME_ENC 0x20 + +#define IPV4ALEN 4 +#define SMALLBUF 256 + +/* There are other fixed-size buffers preventing + this from being increased to 16110. The buffer + sizes would need to be properly de-coupled from + the default MRU. For now, getting up to 1500 is + enough. */ +#define ETH_JUMBO_LEN 1508 + +/* A PPPoE Packet, including Ethernet headers */ +typedef struct PPPoEPacketStruct { + struct ethhdr ethHdr; /* Ethernet header */ + unsigned int vertype:8; /* PPPoE Version and Type (must both be 1) */ + unsigned int code:8; /* PPPoE code */ + unsigned int session:16; /* PPPoE session */ + unsigned int length:16; /* Payload length */ + unsigned char payload[ETH_JUMBO_LEN]; /* A bit of room to spare */ +} PPPoEPacket; + +#define PPPOE_VER(vt) ((vt) >> 4) +#define PPPOE_TYPE(vt) ((vt) & 0xf) +#define PPPOE_VER_TYPE(v, t) (((v) << 4) | (t)) + +/* Header size of a PPPoE packet */ +#define PPPOE_OVERHEAD 6 /* type, code, session, length */ +#define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD) +#define MAX_PPPOE_PAYLOAD (ETH_JUMBO_LEN - PPPOE_OVERHEAD) +#define PPP_OVERHEAD 2 /* protocol */ +#define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - PPP_OVERHEAD) +#define TOTAL_OVERHEAD (PPPOE_OVERHEAD + PPP_OVERHEAD) +#define ETH_PPPOE_MTU (ETH_DATA_LEN - TOTAL_OVERHEAD) + +/* PPPoE Tag */ + +typedef struct PPPoETagStruct { + unsigned int type:16; /* tag type */ + unsigned int length:16; /* Length of payload */ + unsigned char payload[ETH_JUMBO_LEN]; /* A LOT of room to spare */ +} PPPoETag; +/* Header size of a PPPoE tag */ +#define TAG_HDR_SIZE 4 + +/* Chunk to read from stdin */ +#define READ_CHUNK 4096 + +/* Function passed to parsePacket */ +typedef void ParseFunc(UINT16_t type, + UINT16_t len, + unsigned char *data, + void *extra); + +#define PPPINITFCS16 0xffff /* Initial FCS value */ + +/* Keep track of the state of a connection -- collect everything in + one spot */ + +typedef struct PPPoEConnectionStruct { + int discoveryState; /* Where we are in discovery */ + int discoverySocket; /* Raw socket for discovery frames */ + int sessionSocket; /* Raw socket for session frames */ + unsigned char myEth[ETH_ALEN]; /* My MAC address */ + unsigned char peerEth[ETH_ALEN]; /* Peer's MAC address */ + unsigned char req_peer_mac[ETH_ALEN]; /* required peer MAC address */ + unsigned char req_peer; /* require mac addr to match req_peer_mac */ + UINT16_t session; /* Session ID */ + char *ifName; /* Interface name */ + char *serviceName; /* Desired service name, if any */ + char *acName; /* Desired AC name, if any */ + int synchronous; /* Use synchronous PPP */ + PPPoETag hostUniq; /* Use Host-Uniq tag */ + int printACNames; /* Just print AC names */ + FILE *debugFile; /* Debug file for dumping packets */ + int numPADOs; /* Number of PADO packets received */ + PPPoETag cookie; /* We have to send this if we get it */ + PPPoETag relayId; /* Ditto */ + int error; /* Error packet received */ + int debug; /* Set to log packets sent and received */ + int discoveryTimeout; /* Timeout for discovery packets */ + int discoveryAttempts; /* Number of discovery attempts */ + int seenMaxPayload; + int mtu; /* Stored MTU */ + int mru; /* Stored MRU */ +} PPPoEConnection; + +/* Structure used to determine acceptable PADO or PADS packet */ +struct PacketCriteria { + PPPoEConnection *conn; + int acNameOK; + int serviceNameOK; + int seenACName; + int seenServiceName; +}; + +/* Function Prototypes */ +UINT16_t etherType(PPPoEPacket *packet); +int openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr); +int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size); +int receivePacket(int sock, PPPoEPacket *pkt, int *size); +void fatalSys(char const *str); +void dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir); +void dumpHex(FILE *fp, unsigned char const *buf, int len); +int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra); +void parseLogErrs(UINT16_t typ, UINT16_t len, unsigned char *data, void *xtra); +void syncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet); +void asyncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet); +void asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss); +void syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss); +char *strDup(char const *str); +void sendPADT(PPPoEConnection *conn, char const *msg); +void sendSessionPacket(PPPoEConnection *conn, + PPPoEPacket *packet, int len); +void initPPP(void); +void clampMSS(PPPoEPacket *packet, char const *dir, int clampMss); +UINT16_t computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr); +UINT16_t pppFCS16(UINT16_t fcs, unsigned char *cp, int len); +void discovery(PPPoEConnection *conn); +unsigned char *findTag(PPPoEPacket *packet, UINT16_t tagType, + PPPoETag *tag); + +void pppoe_printpkt(PPPoEPacket *packet, + void (*printer)(void *, char *, ...), void *arg); +void pppoe_log_packet(const char *prefix, PPPoEPacket *packet); + +static inline int parseHostUniq(const char *uniq, PPPoETag *tag) +{ + unsigned i, len = strlen(uniq); + +#define hex(x) \ + (((x) <= '9') ? ((x) - '0') : \ + (((x) <= 'F') ? ((x) - 'A' + 10) : \ + ((x) - 'a' + 10))) + + if (!len || len % 2 || len / 2 > sizeof(tag->payload)) + return 0; + + for (i = 0; i < len; i += 2) { + if (!isxdigit(uniq[i]) || !isxdigit(uniq[i+1])) + return 0; + + tag->payload[i / 2] = (char)(hex(uniq[i]) << 4 | hex(uniq[i+1])); + } + +#undef hex + + tag->type = htons(TAG_HOST_UNIQ); + tag->length = htons(len / 2); + return 1; +} + +#define SET_STRING(var, val) do { if (var) free(var); var = strDup(val); } while(0); + +#define CHECK_ROOM(cursor, start, len) \ +do {\ + if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \ + error("Would create too-long packet"); \ + return; \ + } \ +} while(0) + +/* True if Ethernet address is broadcast or multicast */ +#define NOT_UNICAST(e) ((e[0] & 0x01) != 0) +#define BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) == 0xFF) +#define NOT_BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) != 0xFF) diff --git a/pppd/plugins/rp-pppoe/.gitignore b/pppd/plugins/rp-pppoe/.gitignore deleted file mode 100644 index 00c7f9c..0000000 --- a/pppd/plugins/rp-pppoe/.gitignore +++ /dev/null @@ -1 +0,0 @@ -pppoe-discovery diff --git a/pppd/plugins/rp-pppoe/Makefile.linux b/pppd/plugins/rp-pppoe/Makefile.linux deleted file mode 100644 index 2c93f4a..0000000 --- a/pppd/plugins/rp-pppoe/Makefile.linux +++ /dev/null @@ -1,64 +0,0 @@ -# Generated automatically from Makefile.in by configure. -#*********************************************************************** -# -# Makefile -# -# Makefile for Roaring Penguin's Linux PPPoE plugin. -# Modified for integration with pppd sources by Paul Mackerras. -# -# Copyright (C) 2001 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. -# -# $Id: Makefile.linux,v 1.8 2008/06/09 08:34:23 paulus Exp $ -#*********************************************************************** - -DESTDIR = $(INSTROOT)@DESTDIR@ -BINDIR = $(DESTDIR)/sbin -LIBDIR = $(DESTDIR)/lib/pppd/$(PPPDVERSION) - -PPPDVERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h) - -INSTALL = install - -# Version is set ONLY IN THE MAKEFILE! Don't delete this! -RP_VERSION=3.8p - -COPTS=-O2 -g -CFLAGS=$(COPTS) -I../../../include '-DRP_VERSION="$(RP_VERSION)"' -all: rp-pppoe.so pppoe-discovery - -pppoe-discovery: pppoe-discovery.o debug.o - $(CC) $(LDFLAGS) -o pppoe-discovery pppoe-discovery.o debug.o - -pppoe-discovery.o: pppoe-discovery.c - $(CC) $(CFLAGS) -I../../.. -c -o pppoe-discovery.o pppoe-discovery.c - -debug.o: debug.c - $(CC) $(CFLAGS) -I../../.. -c -o debug.o debug.c - -rp-pppoe.so: plugin.o discovery.o if.o common.o - $(CC) $(LDFLAGS) -o rp-pppoe.so -shared plugin.o discovery.o if.o common.o - -install: all - $(INSTALL) -d -m 755 $(LIBDIR) - $(INSTALL) -c -m 4550 rp-pppoe.so $(LIBDIR) - $(INSTALL) -d -m 755 $(BINDIR) - $(INSTALL) -c -m 555 pppoe-discovery $(BINDIR) - -clean: - rm -f *.o *.so pppoe-discovery - -plugin.o: plugin.c - $(CC) $(CFLAGS) -I../../.. -c -o plugin.o -fPIC plugin.c - -discovery.o: discovery.c - $(CC) $(CFLAGS) -I../../.. -c -o discovery.o -fPIC discovery.c - -if.o: if.c - $(CC) $(CFLAGS) -I../../.. -c -o if.o -fPIC if.c - -common.o: common.c - $(CC) $(CFLAGS) -I../../.. -c -o common.o -fPIC common.c - diff --git a/pppd/plugins/rp-pppoe/common.c b/pppd/plugins/rp-pppoe/common.c deleted file mode 100644 index 9ea7fd6..0000000 --- a/pppd/plugins/rp-pppoe/common.c +++ /dev/null @@ -1,282 +0,0 @@ -/*********************************************************************** -* -* 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: common.c,v 1.3 2008/06/09 08:34:23 paulus Exp $"; - -#define _GNU_SOURCE 1 -#include "pppoe.h" -#include "pppd/pppd.h" - -#include -#include -#include -#include /* for LOG_DEBUG */ - -#ifdef HAVE_UNISTD_H -#include -#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 (PPPOE_VER(packet->vertype) != 1) { - error("Invalid PPPoE version (%d)", PPPOE_VER(packet->vertype)); - return -1; - } - if (PPPOE_TYPE(packet->vertype) != 1) { - error("Invalid PPPoE type (%d)", PPPOE_TYPE(packet->vertype)); - return -1; - } - - /* Do some sanity checks on packet */ - if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */ - error("Invalid PPPoE packet length (%u)", len); - return -1; - } - - /* Step through the tags */ - curTag = packet->payload; - while (curTag - packet->payload + TAG_HDR_SIZE <= len) { - /* Alignment is not guaranteed, so do this by hand... */ - tagType = (curTag[0] << 8) + curTag[1]; - tagLen = (curTag[2] << 8) + curTag[3]; - if (tagType == TAG_END_OF_LIST) { - return 0; - } - if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { - error("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: 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.vertype = PPPOE_VER_TYPE(1, 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->hostUniq.length) { - int len = ntohs(conn->hostUniq.length); - memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); - cursor += len + TAG_HDR_SIZE; - plen += len + 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)); - info("Sent PADT"); -} - -#define EH(x) (x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5] - -/* Print out a PPPOE packet for debugging */ -void pppoe_printpkt(PPPoEPacket *packet, - void (*printer)(void *, char *, ...), void *arg) -{ - int len = ntohs(packet->length); - int i, tag, tlen, text; - - switch (ntohs(packet->ethHdr.h_proto)) { - case ETH_PPPOE_DISCOVERY: - printer(arg, "PPPOE Discovery V%dT%d ", PPPOE_VER(packet->vertype), - PPPOE_TYPE(packet->vertype)); - switch (packet->code) { - case CODE_PADI: - printer(arg, "PADI"); - break; - case CODE_PADO: - printer(arg, "PADO"); - break; - case CODE_PADR: - printer(arg, "PADR"); - break; - case CODE_PADS: - printer(arg, "PADS"); - break; - case CODE_PADT: - printer(arg, "PADT"); - break; - default: - printer(arg, "unknown code %x", packet->code); - } - printer(arg, " session 0x%x length %d\n", ntohs(packet->session), len); - break; - case ETH_PPPOE_SESSION: - printer(arg, "PPPOE Session V%dT%d", PPPOE_VER(packet->vertype), - PPPOE_TYPE(packet->vertype)); - printer(arg, " code 0x%x session 0x%x length %d\n", packet->code, - ntohs(packet->session), len); - break; - default: - printer(arg, "Unknown ethernet frame with proto = 0x%x\n", - ntohs(packet->ethHdr.h_proto)); - } - - printer(arg, " dst %02x:%02x:%02x:%02x:%02x:%02x ", EH(packet->ethHdr.h_dest)); - printer(arg, " src %02x:%02x:%02x:%02x:%02x:%02x\n", EH(packet->ethHdr.h_source)); - if (ntohs(packet->ethHdr.h_proto) != ETH_PPPOE_DISCOVERY) - return; - - for (i = 0; i + TAG_HDR_SIZE <= len; i += tlen) { - tag = (packet->payload[i] << 8) + packet->payload[i+1]; - tlen = (packet->payload[i+2] << 8) + packet->payload[i+3]; - if (i + tlen + TAG_HDR_SIZE > len) - break; - text = 0; - i += TAG_HDR_SIZE; - printer(arg, " ["); - switch (tag) { - case TAG_END_OF_LIST: - printer(arg, "end-of-list"); - break; - case TAG_SERVICE_NAME: - printer(arg, "service-name"); - text = 1; - break; - case TAG_AC_NAME: - printer(arg, "AC-name"); - text = 1; - break; - case TAG_HOST_UNIQ: - printer(arg, "host-uniq"); - break; - case TAG_AC_COOKIE: - printer(arg, "AC-cookie"); - break; - case TAG_VENDOR_SPECIFIC: - printer(arg, "vendor-specific"); - break; - case TAG_RELAY_SESSION_ID: - printer(arg, "relay-session-id"); - break; - case TAG_PPP_MAX_PAYLOAD: - printer(arg, "PPP-max-payload"); - break; - case TAG_SERVICE_NAME_ERROR: - printer(arg, "service-name-error"); - text = 1; - break; - case TAG_AC_SYSTEM_ERROR: - printer(arg, "AC-system-error"); - text = 1; - break; - case TAG_GENERIC_ERROR: - printer(arg, "generic-error"); - text = 1; - break; - default: - printer(arg, "unknown tag 0x%x", tag); - } - if (tlen) { - if (text) - printer(arg, " %.*v", tlen, &packet->payload[i]); - else if (tlen <= 32) - printer(arg, " %.*B", tlen, &packet->payload[i]); - else - printer(arg, " %.32B... (length %d)", - &packet->payload[i], tlen); - } - printer(arg, "]"); - } - printer(arg, "\n"); -} - -void pppoe_log_packet(const char *prefix, PPPoEPacket *packet) -{ - init_pr_log(prefix, LOG_DEBUG); - pppoe_printpkt(packet, pr_log, NULL); - end_pr_log(); -} diff --git a/pppd/plugins/rp-pppoe/config.h b/pppd/plugins/rp-pppoe/config.h deleted file mode 100644 index a708859..0000000 --- a/pppd/plugins/rp-pppoe/config.h +++ /dev/null @@ -1,129 +0,0 @@ -/* config.h. Generated automatically by configure. */ -/* config.h.in. Generated automatically from configure.in by autoheader. */ - -/* Define to empty if the keyword does not work. */ -/* #undef const */ - -/* Define if you have that is POSIX.1 compatible. */ -#define HAVE_SYS_WAIT_H 1 - -/* Define to `int' if doesn't define. */ -/* #undef pid_t */ - -/* Define as the return type of signal handlers (int or void). */ -#define RETSIGTYPE void - -/* Define if the setvbuf function takes the buffering type as its second - argument and the buffer pointer as the third, as on System V - before release 3. */ -/* #undef SETVBUF_REVERSED */ - -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define if you can safely include both and . */ -#define TIME_WITH_SYS_TIME 1 - -/* Define if your declares struct tm. */ -/* #undef TM_IN_SYS_TIME */ - -#define HAVE_STRUCT_SOCKADDR_LL 1 - -/* The number of bytes in a unsigned int. */ -#define SIZEOF_UNSIGNED_INT 4 - -/* The number of bytes in a unsigned long. */ -#define SIZEOF_UNSIGNED_LONG 4 - -/* The number of bytes in a unsigned short. */ -#define SIZEOF_UNSIGNED_SHORT 2 - -/* Define if you have the select function. */ -#define HAVE_SELECT 1 - -/* Define if you have the socket function. */ -#define HAVE_SOCKET 1 - -/* Define if you have the strerror function. */ -#define HAVE_STRERROR 1 - -/* Define if you have the strtol function. */ -#define HAVE_STRTOL 1 - -/* Define if you have the header file. */ -#define HAVE_ASM_TYPES_H 1 - -/* Define if you have the header file. */ -#define HAVE_FCNTL_H 1 - -/* Define if you have the header file. */ -#define HAVE_GETOPT_H 1 - -/* Define if you have the header file. */ -#define HAVE_LINUX_IF_ETHER_H 1 - -/* Define if you have kernel-mode PPPoE in Linux file. */ -#define HAVE_LINUX_KERNEL_PPPOE 1 - -/* Define if you have the header file. */ -#define HAVE_LINUX_IF_PACKET_H 1 - -/* Define if you have the header file. */ -#define HAVE_LINUX_IF_PPPOX_H 1 - -/* Define if you have the header file. */ -#define HAVE_NET_BPF_H 1 - -/* Define if you have the header file. */ -#define HAVE_NET_IF_ARP_H 1 - -/* Define if you have the header file. */ -#define HAVE_NET_ETHERNET_H 1 - -/* Define if you have the header file. */ -#define HAVE_NET_IF_H 1 - -/* Define if you have the header file. */ -#define HAVE_LINUX_IF_H 1 - -/* Define if you have the header file. */ -/* #undef HAVE_NET_IF_DL_H */ - -/* Define if you have the header file. */ -/* #undef HAVE_NET_IF_ETHER_H */ - -/* Define if you have the header file. */ -/* #undef HAVE_NET_IF_TYPES_H */ - -/* Define if you have the header file. */ -#define HAVE_NETINET_IF_ETHER_H 1 - -/* Define if you have the header file. */ -#define HAVE_NETPACKET_PACKET_H 1 - -/* Define if you have the header file. */ -/* #undef HAVE_SYS_DLPI_H */ - -/* Define if you have the header file. */ -#define HAVE_SYS_IOCTL_H 1 - -/* Define if you have the header file. */ -#define HAVE_SYS_PARAM_H 1 - -/* Define if you have the header file. */ -#define HAVE_SYS_SOCKET_H 1 - -/* Define if you have the header file. */ -#define HAVE_SYS_TIME_H 1 - -/* Define if you have the header file. */ -#define HAVE_SYS_UIO_H 1 - -/* Define if you have the header file. */ -#define HAVE_SYSLOG_H 1 - -/* Define if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define if you have the N_HDLC line discipline in linux/termios.h */ -#define HAVE_N_HDLC 1 diff --git a/pppd/plugins/rp-pppoe/debug.c b/pppd/plugins/rp-pppoe/debug.c deleted file mode 100644 index a6e6981..0000000 --- a/pppd/plugins/rp-pppoe/debug.c +++ /dev/null @@ -1,145 +0,0 @@ -/*********************************************************************** -* -* debug.c -* -* Implementation of user-space PPPoE redirector for Linux. -* -* Functions for printing debugging information -* -* 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: debug.c,v 1.2 2008/06/09 08:34:23 paulus Exp $"; - -#include "pppoe.h" -#include -#include -#include -#include - -/********************************************************************** -*%FUNCTION: dumpHex -*%ARGUMENTS: -* fp -- file to dump to -* buf -- buffer to dump -* len -- length of data -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Dumps buffer to fp in an easy-to-read format -***********************************************************************/ -void -dumpHex(FILE *fp, unsigned char const *buf, int len) -{ - int i; - int base; - - if (!fp) return; - - /* do NOT dump PAP packets */ - if (len >= 2 && buf[0] == 0xC0 && buf[1] == 0x23) { - fprintf(fp, "(PAP Authentication Frame -- Contents not dumped)\n"); - return; - } - - for (base=0; baselength); - - /* Sheesh... printing times is a pain... */ - struct timeval tv; - time_t now; - int millisec; - struct tm *lt; - char timebuf[256]; - - UINT16_t type = etherType(packet); - if (!fp) return; - gettimeofday(&tv, NULL); - now = (time_t) tv.tv_sec; - millisec = tv.tv_usec / 1000; - lt = localtime(&now); - strftime(timebuf, 256, "%H:%M:%S", lt); - fprintf(fp, "%s.%03d %s PPPoE ", timebuf, millisec, dir); - if (type == Eth_PPPOE_Discovery) { - fprintf(fp, "Discovery (%x) ", (unsigned) type); - } else if (type == Eth_PPPOE_Session) { - fprintf(fp, "Session (%x) ", (unsigned) type); - } else { - fprintf(fp, "Unknown (%x) ", (unsigned) type); - } - - switch(packet->code) { - case CODE_PADI: fprintf(fp, "PADI "); break; - case CODE_PADO: fprintf(fp, "PADO "); break; - case CODE_PADR: fprintf(fp, "PADR "); break; - case CODE_PADS: fprintf(fp, "PADS "); break; - case CODE_PADT: fprintf(fp, "PADT "); break; - case CODE_PADM: fprintf(fp, "PADM "); break; - case CODE_PADN: fprintf(fp, "PADN "); break; - case CODE_SESS: fprintf(fp, "SESS "); break; - } - - fprintf(fp, "sess-id %d length %d\n", - (int) ntohs(packet->session), - len); - - /* Ugly... I apologize... */ - fprintf(fp, - "SourceAddr %02x:%02x:%02x:%02x:%02x:%02x " - "DestAddr %02x:%02x:%02x:%02x:%02x:%02x\n", - (unsigned) packet->ethHdr.h_source[0], - (unsigned) packet->ethHdr.h_source[1], - (unsigned) packet->ethHdr.h_source[2], - (unsigned) packet->ethHdr.h_source[3], - (unsigned) packet->ethHdr.h_source[4], - (unsigned) packet->ethHdr.h_source[5], - (unsigned) packet->ethHdr.h_dest[0], - (unsigned) packet->ethHdr.h_dest[1], - (unsigned) packet->ethHdr.h_dest[2], - (unsigned) packet->ethHdr.h_dest[3], - (unsigned) packet->ethHdr.h_dest[4], - (unsigned) packet->ethHdr.h_dest[5]); - dumpHex(fp, packet->payload, ntohs(packet->length)); -} diff --git a/pppd/plugins/rp-pppoe/discovery.c b/pppd/plugins/rp-pppoe/discovery.c deleted file mode 100644 index 23089df..0000000 --- a/pppd/plugins/rp-pppoe/discovery.c +++ /dev/null @@ -1,666 +0,0 @@ -/*********************************************************************** -* -* discovery.c -* -* Perform PPPoE discovery -* -* Copyright (C) 1999 by Roaring Penguin Software Inc. -* -***********************************************************************/ - -static char const RCSID[] = -"$Id: discovery.c,v 1.6 2008/06/15 04:35:50 paulus Exp $"; - -#define _GNU_SOURCE 1 -#include "pppoe.h" -#include "pppd/pppd.h" -#include "pppd/fsm.h" -#include "pppd/lcp.h" - -#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 - -/* Calculate time remaining until *exp, return 0 if now >= *exp */ -static int time_left(struct timeval *diff, struct timeval *exp) -{ - struct timeval now; - - if (get_time(&now) < 0) { - error("get_time: %m"); - return 0; - } - - if (now.tv_sec > exp->tv_sec - || (now.tv_sec == exp->tv_sec && now.tv_usec >= exp->tv_usec)) - return 0; - - diff->tv_sec = exp->tv_sec - now.tv_sec; - diff->tv_usec = exp->tv_usec - now.tv_usec; - if (diff->tv_usec < 0) { - diff->tv_usec += 1000000; - --diff->tv_sec; - } - - return 1; -} - -/********************************************************************** -*%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. -***********************************************************************/ -static void -parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data, - void *extra) -{ - PPPoETag *tag = extra; - - if (type == TAG_HOST_UNIQ && len == ntohs(tag->length)) - tag->length = memcmp(data, tag->payload, len); -} - -/********************************************************************** -*%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. -***********************************************************************/ -static int -packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) -{ - PPPoETag hostUniq = conn->hostUniq; - - /* If packet is not directed to our MAC address, forget it */ - if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; - - /* If we're not using the Host-Unique tag, then accept the packet */ - if (!conn->hostUniq.length) return 1; - - parsePacket(packet, parseForHostUniq, &hostUniq); - return !hostUniq.length; -} - -/********************************************************************** -*%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 -***********************************************************************/ -static void -parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data, - void *extra) -{ - struct PacketCriteria *pc = (struct PacketCriteria *) extra; - PPPoEConnection *conn = pc->conn; - UINT16_t mru; - int i; - - switch(type) { - case TAG_AC_NAME: - pc->seenACName = 1; - if (conn->printACNames) { - info("Access-Concentrator: %.*s", (int) len, data); - } - if (conn->acName && len == strlen(conn->acName) && - !strncmp((char *) data, conn->acName, len)) { - pc->acNameOK = 1; - } - break; - case TAG_SERVICE_NAME: - pc->seenServiceName = 1; - if (conn->serviceName && len == strlen(conn->serviceName) && - !strncmp((char *) data, conn->serviceName, len)) { - pc->serviceNameOK = 1; - } - break; - case TAG_AC_COOKIE: - conn->cookie.type = htons(type); - conn->cookie.length = htons(len); - memcpy(conn->cookie.payload, data, len); - break; - case TAG_RELAY_SESSION_ID: - conn->relayId.type = htons(type); - conn->relayId.length = htons(len); - memcpy(conn->relayId.payload, data, len); - break; - case TAG_PPP_MAX_PAYLOAD: - if (len == sizeof(mru)) { - memcpy(&mru, data, sizeof(mru)); - mru = ntohs(mru); - if (mru >= ETH_PPPOE_MTU) { - if (lcp_allowoptions[0].mru > mru) - lcp_allowoptions[0].mru = mru; - if (lcp_wantoptions[0].mru > mru) - lcp_wantoptions[0].mru = mru; - conn->seenMaxPayload = 1; - } - } - break; - case TAG_SERVICE_NAME_ERROR: - error("PADO: Service-Name-Error: %.*s", (int) len, data); - conn->error = 1; - break; - case TAG_AC_SYSTEM_ERROR: - error("PADO: System-Error: %.*s", (int) len, data); - conn->error = 1; - break; - case TAG_GENERIC_ERROR: - error("PADO: Generic-Error: %.*s", (int) len, data); - conn->error = 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 -***********************************************************************/ -static void -parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data, - void *extra) -{ - PPPoEConnection *conn = (PPPoEConnection *) extra; - UINT16_t mru; - switch(type) { - case TAG_SERVICE_NAME: - dbglog("PADS: Service-Name: '%.*s'", (int) len, data); - break; - case TAG_PPP_MAX_PAYLOAD: - if (len == sizeof(mru)) { - memcpy(&mru, data, sizeof(mru)); - mru = ntohs(mru); - if (mru >= ETH_PPPOE_MTU) { - if (lcp_allowoptions[0].mru > mru) - lcp_allowoptions[0].mru = mru; - if (lcp_wantoptions[0].mru > mru) - lcp_wantoptions[0].mru = mru; - conn->seenMaxPayload = 1; - } - } - break; - case TAG_SERVICE_NAME_ERROR: - error("PADS: Service-Name-Error: %.*s", (int) len, data); - conn->error = 1; - break; - case TAG_AC_SYSTEM_ERROR: - error("PADS: System-Error: %.*s", (int) len, data); - conn->error = 1; - break; - case TAG_GENERIC_ERROR: - error("PADS: Generic-Error: %.*s", (int) len, data); - conn->error = 1; - break; - 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 -***********************************************************************/ -static void -sendPADI(PPPoEConnection *conn) -{ - PPPoEPacket packet; - unsigned char *cursor = packet.payload; - PPPoETag *svc = (PPPoETag *) (&packet.payload); - UINT16_t namelen = 0; - UINT16_t plen; - int omit_service_name = 0; - - if (conn->serviceName) { - namelen = (UINT16_t) strlen(conn->serviceName); - if (!strcmp(conn->serviceName, "NO-SERVICE-NAME-NON-RFC-COMPLIANT")) { - omit_service_name = 1; - } - } - - /* 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.vertype = PPPOE_VER_TYPE(1, 1); - packet.code = CODE_PADI; - packet.session = 0; - - if (!omit_service_name) { - plen = TAG_HDR_SIZE + namelen; - CHECK_ROOM(cursor, packet.payload, plen); - - svc->type = TAG_SERVICE_NAME; - svc->length = htons(namelen); - - if (conn->serviceName) { - memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); - } - cursor += namelen + TAG_HDR_SIZE; - } else { - plen = 0; - } - - /* If we're using Host-Uniq, copy it over */ - if (conn->hostUniq.length) { - int len = ntohs(conn->hostUniq.length); - CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); - memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); - cursor += len + TAG_HDR_SIZE; - plen += len + TAG_HDR_SIZE; - } - - /* Add our maximum MTU/MRU */ - if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) { - PPPoETag maxPayload; - UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru)); - maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); - maxPayload.length = htons(sizeof(mru)); - memcpy(maxPayload.payload, &mru, sizeof(mru)); - CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE); - memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); - cursor += sizeof(mru) + TAG_HDR_SIZE; - plen += sizeof(mru) + TAG_HDR_SIZE; - } - - packet.length = htons(plen); - - sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); -} - -/********************************************************************** -*%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; - struct timeval expire_at; - - PPPoEPacket packet; - int len; - - struct PacketCriteria pc; - pc.conn = conn; - pc.acNameOK = (conn->acName) ? 0 : 1; - pc.serviceNameOK = (conn->serviceName) ? 0 : 1; - pc.seenACName = 0; - pc.seenServiceName = 0; - conn->seenMaxPayload = 0; - conn->error = 0; - - if (get_time(&expire_at) < 0) { - error("get_time (waitForPADO): %m"); - return; - } - expire_at.tv_sec += timeout; - - do { - if (BPF_BUFFER_IS_EMPTY) { - if (!time_left(&tv, &expire_at)) - return; /* Timed out */ - - FD_ZERO(&readable); - FD_SET(conn->discoverySocket, &readable); - - while(1) { - r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); - if (r >= 0 || errno != EINTR || got_sigterm) break; - } - if (r < 0) { - error("select (waitForPADO): %m"); - return; - } - if (r == 0) - return; /* Timed out */ - } - - /* Get the packet */ - receivePacket(conn->discoverySocket, &packet, &len); - - /* Check length */ - if (ntohs(packet.length) + HDR_SIZE > len) { - error("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 it's not for us, loop again */ - if (!packetIsForMe(conn, &packet)) continue; - - if (packet.code == CODE_PADO) { - if (NOT_UNICAST(packet.ethHdr.h_source)) { - error("Ignoring PADO packet from non-unicast MAC address"); - continue; - } - if (conn->req_peer - && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) { - warn("Ignoring PADO packet from wrong MAC address"); - continue; - } - if (parsePacket(&packet, parsePADOTags, &pc) < 0) - return; - if (conn->error) - return; - if (!pc.seenACName) { - error("Ignoring PADO packet with no AC-Name tag"); - continue; - } - if (!pc.seenServiceName) { - error("Ignoring PADO packet with no Service-Name tag"); - continue; - } - conn->numPADOs++; - if (pc.acNameOK && pc.serviceNameOK) { - memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); - 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 -***********************************************************************/ -static 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.vertype = PPPOE_VER_TYPE(1, 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->hostUniq.length) { - int len = ntohs(conn->hostUniq.length); - CHECK_ROOM(cursor, packet.payload, len+TAG_HDR_SIZE); - memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); - cursor += len + TAG_HDR_SIZE; - plen += len + TAG_HDR_SIZE; - } - - /* Add our maximum MTU/MRU */ - if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) { - PPPoETag maxPayload; - UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru)); - maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); - maxPayload.length = htons(sizeof(mru)); - memcpy(maxPayload.payload, &mru, sizeof(mru)); - CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE); - memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); - cursor += sizeof(mru) + TAG_HDR_SIZE; - plen += sizeof(mru) + 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)); -} - -/********************************************************************** -*%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 -***********************************************************************/ -static void -waitForPADS(PPPoEConnection *conn, int timeout) -{ - fd_set readable; - int r; - struct timeval tv; - struct timeval expire_at; - - PPPoEPacket packet; - int len; - - if (get_time(&expire_at) < 0) { - error("get_time (waitForPADS): %m"); - return; - } - expire_at.tv_sec += timeout; - - conn->error = 0; - do { - if (BPF_BUFFER_IS_EMPTY) { - if (!time_left(&tv, &expire_at)) - return; /* Timed out */ - - FD_ZERO(&readable); - FD_SET(conn->discoverySocket, &readable); - - while(1) { - r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); - if (r >= 0 || errno != EINTR || got_sigterm) break; - } - if (r < 0) { - error("select (waitForPADS): %m"); - return; - } - if (r == 0) - return; /* Timed out */ - } - - /* Get the packet */ - receivePacket(conn->discoverySocket, &packet, &len); - - /* Check length */ - if (ntohs(packet.length) + HDR_SIZE > len) { - error("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 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 */ - if (parsePacket(&packet, parsePADSTags, conn) < 0) - return; - if (conn->error) - return; - 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; - - 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) { - error("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 = conn->discoveryTimeout; - - do { - padiAttempts++; - if (got_sigterm || padiAttempts > conn->discoveryAttempts) { - warn("Timeout waiting for PADO packets"); - close(conn->discoverySocket); - conn->discoverySocket = -1; - return; - } - sendPADI(conn); - conn->discoveryState = STATE_SENT_PADI; - waitForPADO(conn, timeout); - - timeout *= 2; - } while (conn->discoveryState == STATE_SENT_PADI); - - timeout = conn->discoveryTimeout; - do { - padrAttempts++; - if (got_sigterm || padrAttempts > conn->discoveryAttempts) { - warn("Timeout waiting for PADS packets"); - close(conn->discoverySocket); - conn->discoverySocket = -1; - return; - } - sendPADR(conn); - conn->discoveryState = STATE_SENT_PADR; - waitForPADS(conn, timeout); - timeout *= 2; - } while (conn->discoveryState == STATE_SENT_PADR); - - if (!conn->seenMaxPayload) { - /* RFC 4638: MUST limit MTU/MRU to 1492 */ - if (lcp_allowoptions[0].mru > ETH_PPPOE_MTU) - lcp_allowoptions[0].mru = ETH_PPPOE_MTU; - if (lcp_wantoptions[0].mru > ETH_PPPOE_MTU) - lcp_wantoptions[0].mru = ETH_PPPOE_MTU; - } - - /* We're done. */ - close(conn->discoverySocket); - conn->discoverySocket = -1; - conn->discoveryState = STATE_SESSION; - return; -} diff --git a/pppd/plugins/rp-pppoe/if.c b/pppd/plugins/rp-pppoe/if.c deleted file mode 100644 index 225dd56..0000000 --- a/pppd/plugins/rp-pppoe/if.c +++ /dev/null @@ -1,242 +0,0 @@ -/*********************************************************************** -* -* if.c -* -* Implementation of user-space PPPoE redirector for Linux. -* -* Functions for opening a raw socket and reading/writing raw Ethernet frames. -* -* 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: if.c,v 1.2 2008/06/09 08:34:23 paulus Exp $"; - -#define _GNU_SOURCE 1 -#include "pppoe.h" -#include "pppd/pppd.h" - -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifdef HAVE_NETPACKET_PACKET_H -#include -#elif defined(HAVE_LINUX_IF_PACKET_H) -#include -#endif - -#ifdef HAVE_ASM_TYPES_H -#include -#endif - -#ifdef HAVE_SYS_IOCTL_H -#include -#endif - -#include -#include -#include - -#ifdef HAVE_NET_IF_ARP_H -#include -#endif - -/* Initialize frame types to RFC 2516 values. Some broken peers apparently - use different frame types... sigh... */ - -UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; -UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; - -/********************************************************************** -*%FUNCTION: etherType -*%ARGUMENTS: -* packet -- a received PPPoE packet -*%RETURNS: -* ethernet packet type (see /usr/include/net/ethertypes.h) -*%DESCRIPTION: -* Checks the ethernet packet header to determine its type. -* We should only be receveing DISCOVERY and SESSION types if the BPF -* is set up correctly. Logs an error if an unexpected type is received. -* Note that the ethernet type names come from "pppoe.h" and the packet -* packet structure names use the LINUX dialect to maintain consistency -* with the rest of this file. See the BSD section of "pppoe.h" for -* translations of the data structure names. -***********************************************************************/ -UINT16_t -etherType(PPPoEPacket *packet) -{ - UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); - if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { - error("Invalid ether type 0x%x", type); - } - return type; -} - -/********************************************************************** -*%FUNCTION: openInterface -*%ARGUMENTS: -* ifname -- name of interface -* type -- Ethernet frame type -* hwaddr -- if non-NULL, set to the hardware address -*%RETURNS: -* A raw socket for talking to the Ethernet card. Exits on error. -*%DESCRIPTION: -* Opens a raw Ethernet socket -***********************************************************************/ -int -openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) -{ - int optval=1; - int fd; - struct ifreq ifr; - int domain, stype; - -#ifdef HAVE_STRUCT_SOCKADDR_LL - struct sockaddr_ll sa; -#else - struct sockaddr sa; -#endif - - memset(&sa, 0, sizeof(sa)); - -#ifdef HAVE_STRUCT_SOCKADDR_LL - domain = PF_PACKET; - stype = SOCK_RAW; -#else - domain = PF_INET; - stype = SOCK_PACKET; -#endif - - if ((fd = socket(domain, stype, htons(type))) < 0) { - /* Give a more helpful message for the common error case */ - if (errno == EPERM) { - fatal("Cannot create raw socket -- pppoe must be run as root."); - } - error("Can't open socket for pppoe: %m"); - return -1; - } - - if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { - error("Can't set socket options for pppoe: %m"); - close(fd); - return -1; - } - - /* Fill in hardware address */ - if (hwaddr) { - strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { - error("Can't get hardware address for %s: %m", ifname); - close(fd); - return -1; - } - memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); -#ifdef ARPHRD_ETHER - if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { - warn("Interface %.16s is not Ethernet", ifname); - } -#endif - if (NOT_UNICAST(hwaddr)) { - fatal("Can't use interface %.16s: it has broadcast/multicast MAC address", - ifname); - } - } - - /* Sanity check on MTU */ - strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { - error("Can't get MTU for %s: %m", ifname); - } else if (ifr.ifr_mtu < ETH_DATA_LEN) { - error("Interface %.16s has MTU of %d -- should be at least %d.", - ifname, ifr.ifr_mtu, ETH_DATA_LEN); - error("This may cause serious connection problems."); - } - -#ifdef HAVE_STRUCT_SOCKADDR_LL - /* Get interface index */ - sa.sll_family = AF_PACKET; - sa.sll_protocol = htons(type); - - strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { - error("Could not get interface index for %s: %m", ifname); - close(fd); - return -1; - } - sa.sll_ifindex = ifr.ifr_ifindex; - -#else - strcpy(sa.sa_data, ifname); -#endif - - /* We're only interested in packets on specified interface */ - if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { - error("Failed to bind to interface %s: %m", ifname); - close(fd); - return -1; - } - - return fd; -} - - -/*********************************************************************** -*%FUNCTION: sendPacket -*%ARGUMENTS: -* sock -- socket to send to -* pkt -- the packet to transmit -* size -- size of packet (in bytes) -*%RETURNS: -* 0 on success; -1 on failure -*%DESCRIPTION: -* Transmits a packet -***********************************************************************/ -int -sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) -{ - int err; - - if (debug) - pppoe_log_packet("Send ", pkt); -#if defined(HAVE_STRUCT_SOCKADDR_LL) - err = send(sock, pkt, size, 0); -#else - struct sockaddr sa; - - strcpy(sa.sa_data, conn->ifName); - err = sendto(sock, pkt, size, 0, &sa, sizeof(sa)); -#endif - if (err < 0) { - error("error sending pppoe packet: %m"); - return -1; - } - return 0; -} - -/*********************************************************************** -*%FUNCTION: receivePacket -*%ARGUMENTS: -* sock -- socket to read from -* pkt -- place to store the received packet -* size -- set to size of packet in bytes -*%RETURNS: -* >= 0 if all OK; < 0 if error -*%DESCRIPTION: -* Receives a packet -***********************************************************************/ -int -receivePacket(int sock, PPPoEPacket *pkt, int *size) -{ - if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { - error("error receiving pppoe packet: %m"); - return -1; - } - if (debug) - pppoe_log_packet("Recv ", pkt); - return 0; -} diff --git a/pppd/plugins/rp-pppoe/plugin.c b/pppd/plugins/rp-pppoe/plugin.c deleted file mode 100644 index 93c0906..0000000 --- a/pppd/plugins/rp-pppoe/plugin.c +++ /dev/null @@ -1,463 +0,0 @@ -/*********************************************************************** -* -* plugin.c -* -* pppd plugin for kernel-mode PPPoE on Linux -* -* Copyright (C) 2001 by Roaring Penguin Software Inc., Michal Ostrowski -* and Jamal Hadi Salim. -* -* Much code and many ideas derived from pppoe plugin by Michal -* Ostrowski and Jamal Hadi Salim, which carries this copyright: -* -* Copyright 2000 Michal Ostrowski , -* Jamal Hadi Salim -* Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr., -* which is based in part on work from Jens Axboe and Paul Mackerras. -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* as published by the Free Software Foundation; either version -* 2 of the License, or (at your option) any later version. -* -***********************************************************************/ - -static char const RCSID[] = -"$Id: plugin.c,v 1.17 2008/06/15 04:35:50 paulus Exp $"; - -#define _GNU_SOURCE 1 -#include "pppoe.h" - -#include "pppd/pppd.h" -#include "pppd/fsm.h" -#include "pppd/lcp.h" -#include "pppd/ipcp.h" -#include "pppd/ccp.h" -/* #include "pppd/pathnames.h" */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef _ROOT_PATH -#define _ROOT_PATH "" -#endif - -#define _PATH_ETHOPT _ROOT_PATH "/etc/ppp/options." - -char pppd_version[] = VERSION; - -/* From sys-linux.c in pppd -- MUST FIX THIS! */ -extern int new_style_driver; - -char *pppd_pppoe_service = NULL; -static char *acName = NULL; -static char *existingSession = NULL; -static int printACNames = 0; -static char *pppoe_reqd_mac = NULL; -unsigned char pppoe_reqd_mac_addr[6]; -static char *host_uniq; -static int pppoe_padi_timeout = PADI_TIMEOUT; -static int pppoe_padi_attempts = MAX_PADI_ATTEMPTS; - -static int PPPoEDevnameHook(char *cmd, char **argv, int doit); -static option_t Options[] = { - { "device name", o_wild, (void *) &PPPoEDevnameHook, - "PPPoE device name", - OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, - devnam}, - { "rp_pppoe_service", o_string, &pppd_pppoe_service, - "Desired PPPoE service name" }, - { "rp_pppoe_ac", o_string, &acName, - "Desired PPPoE access concentrator name" }, - { "rp_pppoe_sess", o_string, &existingSession, - "Attach to existing session (sessid:macaddr)" }, - { "rp_pppoe_verbose", o_int, &printACNames, - "Be verbose about discovered access concentrators"}, - { "pppoe-mac", o_string, &pppoe_reqd_mac, - "Only connect to specified MAC address" }, - { "host-uniq", o_string, &host_uniq, - "Set the Host-Uniq to the supplied hex string" }, - { "pppoe-padi-timeout", o_int, &pppoe_padi_timeout, - "Initial timeout for discovery packets in seconds" }, - { "pppoe-padi-attempts", o_int, &pppoe_padi_attempts, - "Number of discovery attempts" }, - { NULL } -}; -int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL; -static PPPoEConnection *conn = NULL; - -/********************************************************************** - * %FUNCTION: PPPOEInitDevice - * %ARGUMENTS: - * None - * %RETURNS: - * - * %DESCRIPTION: - * Initializes PPPoE device. - ***********************************************************************/ -static int -PPPOEInitDevice(void) -{ - conn = malloc(sizeof(PPPoEConnection)); - if (!conn) { - novm("PPPoE session data"); - } - memset(conn, 0, sizeof(PPPoEConnection)); - conn->ifName = devnam; - conn->discoverySocket = -1; - conn->sessionSocket = -1; - conn->printACNames = printACNames; - conn->discoveryTimeout = pppoe_padi_timeout; - conn->discoveryAttempts = pppoe_padi_attempts; - return 1; -} - -/********************************************************************** - * %FUNCTION: PPPOEConnectDevice - * %ARGUMENTS: - * None - * %RETURNS: - * Non-negative if all goes well; -1 otherwise - * %DESCRIPTION: - * Connects PPPoE device. - ***********************************************************************/ -static int -PPPOEConnectDevice(void) -{ - struct sockaddr_pppox sp; - struct ifreq ifr; - int s; - - /* Open session socket before discovery phase, to avoid losing session */ - /* packets sent by peer just after PADS packet (noted on some Cisco */ - /* server equipment). */ - /* Opening this socket just before waitForPADS in the discovery() */ - /* function would be more appropriate, but it would mess-up the code */ - conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE); - if (conn->sessionSocket < 0) { - error("Failed to create PPPoE socket: %m"); - return -1; - } - - /* Restore configuration */ - lcp_allowoptions[0].mru = conn->mtu; - lcp_wantoptions[0].mru = conn->mru; - - /* Update maximum MRU */ - s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) { - error("Can't get MTU for %s: %m", conn->ifName); - goto errout; - } - strlcpy(ifr.ifr_name, conn->ifName, sizeof(ifr.ifr_name)); - if (ioctl(s, SIOCGIFMTU, &ifr) < 0) { - error("Can't get MTU for %s: %m", conn->ifName); - close(s); - goto errout; - } - close(s); - - if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) - lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; - if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD) - lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD; - - if (host_uniq) { - if (!parseHostUniq(host_uniq, &conn->hostUniq)) - fatal("Illegal value for host-uniq option"); - } else { - /* if a custom host-uniq is not supplied, use our PID */ - pid_t pid = getpid(); - conn->hostUniq.type = htons(TAG_HOST_UNIQ); - conn->hostUniq.length = htons(sizeof(pid)); - memcpy(conn->hostUniq.payload, &pid, sizeof(pid)); - } - - conn->acName = acName; - conn->serviceName = pppd_pppoe_service; - strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); - if (existingSession) { - unsigned int mac[ETH_ALEN]; - int i, ses; - if (sscanf(existingSession, "%d:%x:%x:%x:%x:%x:%x", - &ses, &mac[0], &mac[1], &mac[2], - &mac[3], &mac[4], &mac[5]) != 7) { - fatal("Illegal value for rp_pppoe_sess option"); - } - conn->session = htons(ses); - for (i=0; ipeerEth[i] = (unsigned char) mac[i]; - } - } else { - conn->discoverySocket = - openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); - discovery(conn); - if (conn->discoveryState != STATE_SESSION) { - error("Unable to complete PPPoE Discovery"); - goto errout; - } - } - - /* Set PPPoE session-number for further consumption */ - ppp_session_number = ntohs(conn->session); - - sp.sa_family = AF_PPPOX; - sp.sa_protocol = PX_PROTO_OE; - sp.sa_addr.pppoe.sid = conn->session; - memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); - memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); - - /* Set remote_number for ServPoET */ - sprintf(remote_number, "%02X:%02X:%02X:%02X:%02X:%02X", - (unsigned) conn->peerEth[0], - (unsigned) conn->peerEth[1], - (unsigned) conn->peerEth[2], - (unsigned) conn->peerEth[3], - (unsigned) conn->peerEth[4], - (unsigned) conn->peerEth[5]); - - warn("Connected to %02X:%02X:%02X:%02X:%02X:%02X via interface %s", - (unsigned) conn->peerEth[0], - (unsigned) conn->peerEth[1], - (unsigned) conn->peerEth[2], - (unsigned) conn->peerEth[3], - (unsigned) conn->peerEth[4], - (unsigned) conn->peerEth[5], - conn->ifName); - - script_setenv("MACREMOTE", remote_number, 0); - - if (connect(conn->sessionSocket, (struct sockaddr *) &sp, - sizeof(struct sockaddr_pppox)) < 0) { - error("Failed to connect PPPoE socket: %d %m", errno); - goto errout; - } - - return conn->sessionSocket; - - errout: - if (conn->discoverySocket >= 0) { - sendPADT(conn, NULL); - close(conn->discoverySocket); - conn->discoverySocket = -1; - } - close(conn->sessionSocket); - return -1; -} - -static void -PPPOERecvConfig(int mru, - u_int32_t asyncmap, - int pcomp, - int accomp) -{ -#if 0 /* broken protocol, but no point harrassing the users I guess... */ - if (mru > MAX_PPPOE_MTU) - warn("Couldn't increase MRU to %d", mru); -#endif -} - -/********************************************************************** - * %FUNCTION: PPPOEDisconnectDevice - * %ARGUMENTS: - * None - * %RETURNS: - * Nothing - * %DESCRIPTION: - * Disconnects PPPoE device - ***********************************************************************/ -static void -PPPOEDisconnectDevice(void) -{ - struct sockaddr_pppox sp; - - sp.sa_family = AF_PPPOX; - sp.sa_protocol = PX_PROTO_OE; - sp.sa_addr.pppoe.sid = 0; - memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ); - memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN); - if (connect(conn->sessionSocket, (struct sockaddr *) &sp, - sizeof(struct sockaddr_pppox)) < 0 && errno != EALREADY) - error("Failed to disconnect PPPoE socket: %d %m", errno); - close(conn->sessionSocket); - if (conn->discoverySocket >= 0) { - sendPADT(conn, NULL); - close(conn->discoverySocket); - } -} - -static void -PPPOEDeviceOptions(void) -{ - char buf[MAXPATHLEN]; - - strlcpy(buf, _PATH_ETHOPT, MAXPATHLEN); - strlcat(buf, devnam, MAXPATHLEN); - if (!options_from_file(buf, 0, 0, 1)) - exit(EXIT_OPTION_ERROR); - -} - -struct channel pppoe_channel; - -/********************************************************************** - * %FUNCTION: PPPoEDevnameHook - * %ARGUMENTS: - * cmd -- the command (actually, the device name - * argv -- argument vector - * doit -- if non-zero, set device name. Otherwise, just check if possible - * %RETURNS: - * 1 if we will handle this device; 0 otherwise. - * %DESCRIPTION: - * Checks if name is a valid interface name; if so, returns 1. Also - * sets up devnam (string representation of device). - ***********************************************************************/ -static int -PPPoEDevnameHook(char *cmd, char **argv, int doit) -{ - int r = 1; - int fd; - struct ifreq ifr; - - /* - * Take any otherwise-unrecognized option as a possible device name, - * and test if it is the name of a network interface with a - * hardware address whose sa_family is ARPHRD_ETHER. - */ - if (strlen(cmd) > 4 && !strncmp(cmd, "nic-", 4)) { - /* Strip off "nic-" */ - cmd += 4; - } - - /* Open a socket */ - if ((fd = socket(PF_PACKET, SOCK_RAW, 0)) < 0) { - r = 0; - } - - /* Try getting interface index */ - if (r) { - strlcpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name)); - if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { - r = 0; - } else { - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { - r = 0; - } else { - if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { - if (doit) - error("Interface %s not Ethernet", cmd); - r = 0; - } - } - } - } - - /* Close socket */ - close(fd); - if (r && doit) { - strlcpy(devnam, cmd, sizeof(devnam)); - if (the_channel != &pppoe_channel) { - - the_channel = &pppoe_channel; - modem = 0; - - PPPOEInitDevice(); - } - return 1; - } - - return r; -} - -/********************************************************************** - * %FUNCTION: plugin_init - * %ARGUMENTS: - * None - * %RETURNS: - * Nothing - * %DESCRIPTION: - * Initializes hooks for pppd plugin - ***********************************************************************/ -void -plugin_init(void) -{ - if (!ppp_available() && !new_style_driver) { - fatal("Linux kernel does not support PPPoE -- are you running 2.4.x?"); - } - - add_options(Options); - - info("RP-PPPoE plugin version %s compiled against pppd %s", - RP_VERSION, VERSION); -} - -void pppoe_check_options(void) -{ - unsigned int mac[6]; - int i; - - if (pppoe_reqd_mac != NULL) { - if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x", - &mac[0], &mac[1], &mac[2], &mac[3], - &mac[4], &mac[5]) != 6) { - option_error("cannot parse pppoe-mac option value"); - exit(EXIT_OPTION_ERROR); - } - for (i = 0; i < 6; ++i) - conn->req_peer_mac[i] = mac[i]; - conn->req_peer = 1; - } - - lcp_allowoptions[0].neg_accompression = 0; - lcp_wantoptions[0].neg_accompression = 0; - - lcp_allowoptions[0].neg_asyncmap = 0; - lcp_wantoptions[0].neg_asyncmap = 0; - - lcp_allowoptions[0].neg_pcompression = 0; - lcp_wantoptions[0].neg_pcompression = 0; - - if (lcp_allowoptions[0].mru > MAX_PPPOE_MTU) - lcp_allowoptions[0].mru = MAX_PPPOE_MTU; - if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU) - lcp_wantoptions[0].mru = MAX_PPPOE_MTU; - - /* Save configuration */ - conn->mtu = lcp_allowoptions[0].mru; - conn->mru = lcp_wantoptions[0].mru; - - ccp_allowoptions[0].deflate = 0; - ccp_wantoptions[0].deflate = 0; - - ipcp_allowoptions[0].neg_vj = 0; - ipcp_wantoptions[0].neg_vj = 0; - - ccp_allowoptions[0].bsd_compress = 0; - ccp_wantoptions[0].bsd_compress = 0; -} - -struct channel pppoe_channel = { - .options = Options, - .process_extra_options = &PPPOEDeviceOptions, - .check_options = pppoe_check_options, - .connect = &PPPOEConnectDevice, - .disconnect = &PPPOEDisconnectDevice, - .establish_ppp = &generic_establish_ppp, - .disestablish_ppp = &generic_disestablish_ppp, - .send_config = NULL, - .recv_config = &PPPOERecvConfig, - .close = NULL, - .cleanup = NULL -}; diff --git a/pppd/plugins/rp-pppoe/pppoe-discovery.c b/pppd/plugins/rp-pppoe/pppoe-discovery.c deleted file mode 100644 index c970337..0000000 --- a/pppd/plugins/rp-pppoe/pppoe-discovery.c +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Perform PPPoE discovery - * - * Copyright (C) 2000-2001 by Roaring Penguin Software Inc. - * Copyright (C) 2004 Marco d'Itri - * - * This program may be distributed according to the terms of the GNU - * General Public License, version 2 or (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "pppoe.h" - -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifdef HAVE_NETPACKET_PACKET_H -#include -#elif defined(HAVE_LINUX_IF_PACKET_H) -#include -#endif - -#ifdef HAVE_ASM_TYPES_H -#include -#endif - -#ifdef HAVE_SYS_IOCTL_H -#include -#endif - -#include -#include -#include - -#ifdef HAVE_NET_IF_ARP_H -#include -#endif - -char *xstrdup(const char *s); -void usage(void); - -void die(int status) -{ - exit(status); -} - -void error(char *fmt, ...) -{ - va_list pvar; - va_start(pvar, fmt); - vfprintf(stderr, fmt, pvar); - va_end(pvar); -} - -/* Initialize frame types to RFC 2516 values. Some broken peers apparently - use different frame types... sigh... */ - -UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; -UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; - -/********************************************************************** -*%FUNCTION: etherType -*%ARGUMENTS: -* packet -- a received PPPoE packet -*%RETURNS: -* ethernet packet type (see /usr/include/net/ethertypes.h) -*%DESCRIPTION: -* Checks the ethernet packet header to determine its type. -* We should only be receveing DISCOVERY and SESSION types if the BPF -* is set up correctly. Logs an error if an unexpected type is received. -* Note that the ethernet type names come from "pppoe.h" and the packet -* packet structure names use the LINUX dialect to maintain consistency -* with the rest of this file. See the BSD section of "pppoe.h" for -* translations of the data structure names. -***********************************************************************/ -UINT16_t -etherType(PPPoEPacket *packet) -{ - UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); - if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { - fprintf(stderr, "Invalid ether type 0x%x\n", type); - } - return type; -} - -/********************************************************************** -*%FUNCTION: openInterface -*%ARGUMENTS: -* ifname -- name of interface -* type -- Ethernet frame type -* hwaddr -- if non-NULL, set to the hardware address -*%RETURNS: -* A raw socket for talking to the Ethernet card. Exits on error. -*%DESCRIPTION: -* Opens a raw Ethernet socket -***********************************************************************/ -int -openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) -{ - int optval=1; - int fd; - struct ifreq ifr; - int domain, stype; - -#ifdef HAVE_STRUCT_SOCKADDR_LL - struct sockaddr_ll sa; -#else - struct sockaddr sa; -#endif - - memset(&sa, 0, sizeof(sa)); - -#ifdef HAVE_STRUCT_SOCKADDR_LL - domain = PF_PACKET; - stype = SOCK_RAW; -#else - domain = PF_INET; - stype = SOCK_PACKET; -#endif - - if ((fd = socket(domain, stype, htons(type))) < 0) { - /* Give a more helpful message for the common error case */ - if (errno == EPERM) { - rp_fatal("Cannot create raw socket -- pppoe must be run as root."); - } - fatalSys("socket"); - } - - if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { - fatalSys("setsockopt"); - } - - /* Fill in hardware address */ - if (hwaddr) { - strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { - fatalSys("ioctl(SIOCGIFHWADDR)"); - } - memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); -#ifdef ARPHRD_ETHER - if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { - char buffer[256]; - sprintf(buffer, "Interface %.16s is not Ethernet", ifname); - rp_fatal(buffer); - } -#endif - if (NOT_UNICAST(hwaddr)) { - char buffer[256]; - sprintf(buffer, - "Interface %.16s has broadcast/multicast MAC address??", - ifname); - rp_fatal(buffer); - } - } - - /* Sanity check on MTU */ - strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { - fatalSys("ioctl(SIOCGIFMTU)"); - } - if (ifr.ifr_mtu < ETH_DATA_LEN) { - fprintf(stderr, "Interface %.16s has MTU of %d -- should be %d.\n", - ifname, ifr.ifr_mtu, ETH_DATA_LEN); - fprintf(stderr, "You may have serious connection problems.\n"); - } - -#ifdef HAVE_STRUCT_SOCKADDR_LL - /* Get interface index */ - sa.sll_family = AF_PACKET; - sa.sll_protocol = htons(type); - - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_name[IFNAMSIZ - 1] = 0; - if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { - fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index"); - } - sa.sll_ifindex = ifr.ifr_ifindex; - -#else - strcpy(sa.sa_data, ifname); -#endif - - /* We're only interested in packets on specified interface */ - if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { - fatalSys("bind"); - } - - return fd; -} - - -/*********************************************************************** -*%FUNCTION: sendPacket -*%ARGUMENTS: -* sock -- socket to send to -* pkt -- the packet to transmit -* size -- size of packet (in bytes) -*%RETURNS: -* 0 on success; -1 on failure -*%DESCRIPTION: -* Transmits a packet -***********************************************************************/ -int -sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) -{ -#if defined(HAVE_STRUCT_SOCKADDR_LL) - if (send(sock, pkt, size, 0) < 0) { - sysErr("send (sendPacket)"); - return -1; - } -#else - struct sockaddr sa; - - if (!conn) { - rp_fatal("relay and server not supported on Linux 2.0 kernels"); - } - strcpy(sa.sa_data, conn->ifName); - if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) { - sysErr("sendto (sendPacket)"); - return -1; - } -#endif - return 0; -} - -/*********************************************************************** -*%FUNCTION: receivePacket -*%ARGUMENTS: -* sock -- socket to read from -* pkt -- place to store the received packet -* size -- set to size of packet in bytes -*%RETURNS: -* >= 0 if all OK; < 0 if error -*%DESCRIPTION: -* Receives a packet -***********************************************************************/ -int -receivePacket(int sock, PPPoEPacket *pkt, int *size) -{ - if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { - sysErr("recv (receivePacket)"); - return -1; - } - return 0; -} - -/********************************************************************** -*%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 (PPPOE_VER(packet->vertype) != 1) { - fprintf(stderr, "Invalid PPPoE version (%d)\n", - PPPOE_VER(packet->vertype)); - return -1; - } - if (PPPOE_TYPE(packet->vertype) != 1) { - fprintf(stderr, "Invalid PPPoE type (%d)\n", - PPPOE_TYPE(packet->vertype)); - return -1; - } - - /* Do some sanity checks on packet */ - if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */ - fprintf(stderr, "Invalid PPPoE packet length (%u)\n", 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 = (curTag[0] << 8) + curTag[1]; - tagLen = (curTag[2] << 8) + curTag[3]; - if (tagType == TAG_END_OF_LIST) { - return 0; - } - if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { - fprintf(stderr, "Invalid PPPoE tag length (%u)\n", tagLen); - return -1; - } - func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); - curTag = curTag + TAG_HDR_SIZE + tagLen; - } - return 0; -} - -/********************************************************************** -*%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) -{ - PPPoETag *tag = extra; - - if (type == TAG_HOST_UNIQ && len == ntohs(tag->length)) - tag->length = memcmp(data, tag->payload, len); -} - -/********************************************************************** -*%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) -{ - PPPoETag hostUniq = conn->hostUniq; - - /* If packet is not directed to our MAC address, forget it */ - if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; - - /* If we're not using the Host-Unique tag, then accept the packet */ - if (!conn->hostUniq.length) return 1; - - parsePacket(packet, parseForHostUniq, &hostUniq); - return !hostUniq.length; -} - -/********************************************************************** -*%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: - pc->seenACName = 1; - 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: - pc->seenServiceName = 1; - 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); - } - break; - case TAG_AC_SYSTEM_ERROR: - if (conn->printACNames) { - printf("Got a System-Error tag: %.*s\n", (int) len, data); - } - break; - case TAG_GENERIC_ERROR: - if (conn->printACNames) { - printf("Got a Generic-Error tag: %.*s\n", (int) len, data); - } - 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.vertype = PPPOE_VER_TYPE(1, 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->hostUniq.length) { - int len = ntohs(conn->hostUniq.length); - CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); - memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); - cursor += len + TAG_HDR_SIZE; - plen += len + 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; - pc.seenACName = 0; - pc.seenServiceName = 0; - conn->error = 0; - - 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) { - perror("select (waitForPADO)"); - return; - } - if (r == 0) return; /* Timed out */ - } - - /* Get the packet */ - receivePacket(conn->discoverySocket, &packet, &len); - - /* Check length */ - if (ntohs(packet.length) + HDR_SIZE > len) { - fprintf(stderr, "Bogus PPPoE length field (%u)\n", - (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 (BROADCAST(packet.ethHdr.h_source)) { - fprintf(stderr, "Ignoring PADO packet from broadcast MAC address\n"); - continue; - } - parsePacket(&packet, parsePADOTags, &pc); - if (conn->error) - return; - if (!pc.seenACName) { - fprintf(stderr, "Ignoring PADO packet with no AC-Name tag\n"); - continue; - } - if (!pc.seenServiceName) { - fprintf(stderr, "Ignoring PADO packet with no Service-Name tag\n"); - continue; - } - conn->numPADOs++; - 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]); - printf("--------------------------------------------------\n"); - continue; - } - conn->discoveryState = STATE_RECEIVED_PADO; - break; - } - } - } while (conn->discoveryState != STATE_RECEIVED_PADO); -} - -/********************************************************************** -*%FUNCTION: discovery -*%ARGUMENTS: -* conn -- PPPoE connection info structure -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Performs the PPPoE discovery phase -***********************************************************************/ -void -discovery(PPPoEConnection *conn) -{ - int padiAttempts = 0; - int timeout = conn->discoveryTimeout; - - conn->discoverySocket = - openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); - - do { - padiAttempts++; - if (padiAttempts > conn->discoveryAttempts) { - fprintf(stderr, "Timeout waiting for PADO packets\n"); - close(conn->discoverySocket); - conn->discoverySocket = -1; - return; - } - sendPADI(conn); - conn->discoveryState = STATE_SENT_PADI; - waitForPADO(conn, timeout); - } while (!conn->numPADOs); -} - -int main(int argc, char *argv[]) -{ - int opt; - PPPoEConnection *conn; - - conn = malloc(sizeof(PPPoEConnection)); - if (!conn) - fatalSys("malloc"); - - memset(conn, 0, sizeof(PPPoEConnection)); - - conn->printACNames = 1; - conn->discoveryTimeout = PADI_TIMEOUT; - conn->discoveryAttempts = MAX_PADI_ATTEMPTS; - - while ((opt = getopt(argc, argv, "I:D:VUQS:C:W:t:a:h")) > 0) { - switch(opt) { - case 'S': - conn->serviceName = xstrdup(optarg); - break; - case 'C': - conn->acName = xstrdup(optarg); - break; - case 't': - if (sscanf(optarg, "%d", &conn->discoveryTimeout) != 1) { - fprintf(stderr, "Illegal argument to -t: Should be -t timeout\n"); - exit(EXIT_FAILURE); - } - if (conn->discoveryTimeout < 1) { - conn->discoveryTimeout = 1; - } - break; - case 'a': - if (sscanf(optarg, "%d", &conn->discoveryAttempts) != 1) { - fprintf(stderr, "Illegal argument to -a: Should be -a attempts\n"); - exit(EXIT_FAILURE); - } - if (conn->discoveryAttempts < 1) { - conn->discoveryAttempts = 1; - } - break; - case 'U': - if(conn->hostUniq.length) { - fprintf(stderr, "-U and -W are mutually exclusive\n"); - exit(EXIT_FAILURE); - } else { - pid_t pid = getpid(); - conn->hostUniq.type = htons(TAG_HOST_UNIQ); - conn->hostUniq.length = htons(sizeof(pid)); - memcpy(conn->hostUniq.payload, &pid, sizeof(pid)); - } - break; - case 'W': - if(conn->hostUniq.length) { - fprintf(stderr, "-U and -W are mutually exclusive\n"); - exit(EXIT_FAILURE); - } - if (!parseHostUniq(optarg, &conn->hostUniq)) { - fprintf(stderr, "Invalid host-uniq argument: %s\n", optarg); - exit(EXIT_FAILURE); - } - break; - case 'D': - conn->debugFile = fopen(optarg, "w"); - if (!conn->debugFile) { - fprintf(stderr, "Could not open %s: %s\n", - optarg, strerror(errno)); - exit(1); - } - fprintf(conn->debugFile, "pppoe-discovery %s\n", RP_VERSION); - break; - case 'I': - conn->ifName = xstrdup(optarg); - break; - case 'Q': - conn->printACNames = 0; - break; - case 'V': - case 'h': - usage(); - exit(0); - default: - usage(); - exit(1); - } - } - - /* default interface name */ - if (!conn->ifName) - conn->ifName = strdup("eth0"); - - conn->discoverySocket = -1; - conn->sessionSocket = -1; - - discovery(conn); - - if (!conn->numPADOs) - exit(1); - else - exit(0); -} - -void rp_fatal(char const *str) -{ - fprintf(stderr, "%s\n", str); - exit(1); -} - -void fatalSys(char const *str) -{ - perror(str); - exit(1); -} - -void sysErr(char const *str) -{ - rp_fatal(str); -} - -char *xstrdup(const char *s) -{ - register char *ret = strdup(s); - if (!ret) - sysErr("strdup"); - return ret; -} - -void usage(void) -{ - fprintf(stderr, "Usage: pppoe-discovery [options]\n"); - fprintf(stderr, "Options:\n"); - fprintf(stderr, " -I if_name -- Specify interface (default eth0)\n"); - fprintf(stderr, " -D filename -- Log debugging information in filename.\n"); - fprintf(stderr, - " -t timeout -- Initial timeout for discovery packets in seconds\n" - " -a attempts -- Number of discovery attempts\n" - " -V -- Print version and exit.\n" - " -Q -- Quit Mode: Do not print access concentrator names\n" - " -S name -- Set desired service name.\n" - " -C name -- Set desired access concentrator name.\n" - " -U -- Use Host-Unique to allow multiple PPPoE sessions.\n" - " -W hexvalue -- Set the Host-Unique to the supplied hex string.\n" - " -h -- Print usage information.\n"); - fprintf(stderr, "\nVersion " RP_VERSION "\n"); -} diff --git a/pppd/plugins/rp-pppoe/pppoe.h b/pppd/plugins/rp-pppoe/pppoe.h deleted file mode 100644 index e2dc2ff..0000000 --- a/pppd/plugins/rp-pppoe/pppoe.h +++ /dev/null @@ -1,331 +0,0 @@ -/*********************************************************************** -* -* pppoe.h -* -* Declaration of various PPPoE constants -* -* Copyright (C) 2000 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. -* -* $Id: pppoe.h,v 1.4 2008/06/15 04:35:50 paulus Exp $ -* -***********************************************************************/ - -#include "config.h" - -#include /* For FILE */ -#include /* For pid_t */ -#include -#include - -#include "pppd/pppd.h" /* For error */ - -/* How do we access raw Ethernet devices? */ -#undef USE_LINUX_PACKET -#undef USE_BPF - -#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) -#define USE_LINUX_PACKET 1 -#elif defined(HAVE_SYS_DLPI_H) -#define USE_DLPI -#elif defined(HAVE_NET_BPF_H) -#define USE_BPF 1 -#endif - -/* Sanity check */ -#if !defined(USE_BPF) && !defined(USE_LINUX_PACKET) && !defined(USE_DLPI) -#error Unknown method for accessing raw Ethernet frames -#endif - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif - -/* This has to be included before Linux 4.8's linux/in.h - * gets dragged in. */ -#include - -/* Ugly header files on some Linux boxes... */ -#if defined(HAVE_LINUX_IF_H) -#include -#elif defined(HAVE_NET_IF_H) -#include -#endif - -#ifdef HAVE_NET_IF_TYPES_H -#include -#endif - -#define BPF_BUFFER_IS_EMPTY 1 -#define BPF_BUFFER_HAS_DATA 0 - -/* Define various integer types -- assumes a char is 8 bits */ -#if SIZEOF_UNSIGNED_SHORT == 2 -typedef unsigned short UINT16_t; -#elif SIZEOF_UNSIGNED_INT == 2 -typedef unsigned int UINT16_t; -#else -#error Could not find a 16-bit integer type -#endif - -#if SIZEOF_UNSIGNED_SHORT == 4 -typedef unsigned short UINT32_t; -#elif SIZEOF_UNSIGNED_INT == 4 -typedef unsigned int UINT32_t; -#elif SIZEOF_UNSIGNED_LONG == 4 -typedef unsigned long UINT32_t; -#else -#error Could not find a 32-bit integer type -#endif - -#ifdef HAVE_LINUX_IF_ETHER_H -#include -#else - -#ifdef HAVE_NETINET_IF_ETHER_H -#include - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifndef HAVE_SYS_DLPI_H -#include -#endif -#endif -#endif - -/* Ethernet frame types according to RFC 2516 */ -#define ETH_PPPOE_DISCOVERY 0x8863 -#define ETH_PPPOE_SESSION 0x8864 - -/* But some brain-dead peers disobey the RFC, so frame types are variables */ -extern UINT16_t Eth_PPPOE_Discovery; -extern UINT16_t Eth_PPPOE_Session; - -/* PPPoE codes */ -#define CODE_PADI 0x09 -#define CODE_PADO 0x07 -#define CODE_PADR 0x19 -#define CODE_PADS 0x65 -#define CODE_PADT 0xA7 - -/* Extensions from draft-carrel-info-pppoe-ext-00 */ -/* I do NOT like PADM or PADN, but they are here for completeness */ -#define CODE_PADM 0xD3 -#define CODE_PADN 0xD4 - -#define CODE_SESS 0x00 - -/* PPPoE Tags */ -#define TAG_END_OF_LIST 0x0000 -#define TAG_SERVICE_NAME 0x0101 -#define TAG_AC_NAME 0x0102 -#define TAG_HOST_UNIQ 0x0103 -#define TAG_AC_COOKIE 0x0104 -#define TAG_VENDOR_SPECIFIC 0x0105 -#define TAG_RELAY_SESSION_ID 0x0110 -#define TAG_PPP_MAX_PAYLOAD 0x0120 -#define TAG_SERVICE_NAME_ERROR 0x0201 -#define TAG_AC_SYSTEM_ERROR 0x0202 -#define TAG_GENERIC_ERROR 0x0203 - -/* Extensions from draft-carrel-info-pppoe-ext-00 */ -/* I do NOT like these tags one little bit */ -#define TAG_HURL 0x111 -#define TAG_MOTM 0x112 -#define TAG_IP_ROUTE_ADD 0x121 - -/* Discovery phase states */ -#define STATE_SENT_PADI 0 -#define STATE_RECEIVED_PADO 1 -#define STATE_SENT_PADR 2 -#define STATE_SESSION 3 -#define STATE_TERMINATED 4 - -/* How many PADI/PADS attempts? */ -#define MAX_PADI_ATTEMPTS 3 - -/* Initial timeout for PADO/PADS */ -#define PADI_TIMEOUT 5 - -/* States for scanning PPP frames */ -#define STATE_WAITFOR_FRAME_ADDR 0 -#define STATE_DROP_PROTO 1 -#define STATE_BUILDING_PACKET 2 - -/* Special PPP frame characters */ -#define FRAME_ESC 0x7D -#define FRAME_FLAG 0x7E -#define FRAME_ADDR 0xFF -#define FRAME_CTRL 0x03 -#define FRAME_ENC 0x20 - -#define IPV4ALEN 4 -#define SMALLBUF 256 - -/* There are other fixed-size buffers preventing - this from being increased to 16110. The buffer - sizes would need to be properly de-coupled from - the default MRU. For now, getting up to 1500 is - enough. */ -#define ETH_JUMBO_LEN 1508 - -/* A PPPoE Packet, including Ethernet headers */ -typedef struct PPPoEPacketStruct { - struct ethhdr ethHdr; /* Ethernet header */ - unsigned int vertype:8; /* PPPoE Version and Type (must both be 1) */ - unsigned int code:8; /* PPPoE code */ - unsigned int session:16; /* PPPoE session */ - unsigned int length:16; /* Payload length */ - unsigned char payload[ETH_JUMBO_LEN]; /* A bit of room to spare */ -} PPPoEPacket; - -#define PPPOE_VER(vt) ((vt) >> 4) -#define PPPOE_TYPE(vt) ((vt) & 0xf) -#define PPPOE_VER_TYPE(v, t) (((v) << 4) | (t)) - -/* Header size of a PPPoE packet */ -#define PPPOE_OVERHEAD 6 /* type, code, session, length */ -#define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD) -#define MAX_PPPOE_PAYLOAD (ETH_JUMBO_LEN - PPPOE_OVERHEAD) -#define PPP_OVERHEAD 2 /* protocol */ -#define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - PPP_OVERHEAD) -#define TOTAL_OVERHEAD (PPPOE_OVERHEAD + PPP_OVERHEAD) -#define ETH_PPPOE_MTU (ETH_DATA_LEN - TOTAL_OVERHEAD) - -/* PPPoE Tag */ - -typedef struct PPPoETagStruct { - unsigned int type:16; /* tag type */ - unsigned int length:16; /* Length of payload */ - unsigned char payload[ETH_JUMBO_LEN]; /* A LOT of room to spare */ -} PPPoETag; -/* Header size of a PPPoE tag */ -#define TAG_HDR_SIZE 4 - -/* Chunk to read from stdin */ -#define READ_CHUNK 4096 - -/* Function passed to parsePacket */ -typedef void ParseFunc(UINT16_t type, - UINT16_t len, - unsigned char *data, - void *extra); - -#define PPPINITFCS16 0xffff /* Initial FCS value */ - -/* Keep track of the state of a connection -- collect everything in - one spot */ - -typedef struct PPPoEConnectionStruct { - int discoveryState; /* Where we are in discovery */ - int discoverySocket; /* Raw socket for discovery frames */ - int sessionSocket; /* Raw socket for session frames */ - unsigned char myEth[ETH_ALEN]; /* My MAC address */ - unsigned char peerEth[ETH_ALEN]; /* Peer's MAC address */ - unsigned char req_peer_mac[ETH_ALEN]; /* required peer MAC address */ - unsigned char req_peer; /* require mac addr to match req_peer_mac */ - UINT16_t session; /* Session ID */ - char *ifName; /* Interface name */ - char *serviceName; /* Desired service name, if any */ - char *acName; /* Desired AC name, if any */ - int synchronous; /* Use synchronous PPP */ - PPPoETag hostUniq; /* Use Host-Uniq tag */ - int printACNames; /* Just print AC names */ - FILE *debugFile; /* Debug file for dumping packets */ - int numPADOs; /* Number of PADO packets received */ - PPPoETag cookie; /* We have to send this if we get it */ - PPPoETag relayId; /* Ditto */ - int error; /* Error packet received */ - int debug; /* Set to log packets sent and received */ - int discoveryTimeout; /* Timeout for discovery packets */ - int discoveryAttempts; /* Number of discovery attempts */ - int seenMaxPayload; - int mtu; /* Stored MTU */ - int mru; /* Stored MRU */ -} PPPoEConnection; - -/* Structure used to determine acceptable PADO or PADS packet */ -struct PacketCriteria { - PPPoEConnection *conn; - int acNameOK; - int serviceNameOK; - int seenACName; - int seenServiceName; -}; - -/* Function Prototypes */ -UINT16_t etherType(PPPoEPacket *packet); -int openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr); -int sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size); -int receivePacket(int sock, PPPoEPacket *pkt, int *size); -void fatalSys(char const *str); -void rp_fatal(char const *str); -void printErr(char const *str); -void sysErr(char const *str); -void dumpPacket(FILE *fp, PPPoEPacket *packet, char const *dir); -void dumpHex(FILE *fp, unsigned char const *buf, int len); -int parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra); -void parseLogErrs(UINT16_t typ, UINT16_t len, unsigned char *data, void *xtra); -void syncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet); -void asyncReadFromPPP(PPPoEConnection *conn, PPPoEPacket *packet); -void asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss); -void syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss); -char *strDup(char const *str); -void sendPADT(PPPoEConnection *conn, char const *msg); -void sendSessionPacket(PPPoEConnection *conn, - PPPoEPacket *packet, int len); -void initPPP(void); -void clampMSS(PPPoEPacket *packet, char const *dir, int clampMss); -UINT16_t computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr); -UINT16_t pppFCS16(UINT16_t fcs, unsigned char *cp, int len); -void discovery(PPPoEConnection *conn); -unsigned char *findTag(PPPoEPacket *packet, UINT16_t tagType, - PPPoETag *tag); - -void pppoe_printpkt(PPPoEPacket *packet, - void (*printer)(void *, char *, ...), void *arg); -void pppoe_log_packet(const char *prefix, PPPoEPacket *packet); - -static inline int parseHostUniq(const char *uniq, PPPoETag *tag) -{ - unsigned i, len = strlen(uniq); - -#define hex(x) \ - (((x) <= '9') ? ((x) - '0') : \ - (((x) <= 'F') ? ((x) - 'A' + 10) : \ - ((x) - 'a' + 10))) - - if (!len || len % 2 || len / 2 > sizeof(tag->payload)) - return 0; - - for (i = 0; i < len; i += 2) { - if (!isxdigit(uniq[i]) || !isxdigit(uniq[i+1])) - return 0; - - tag->payload[i / 2] = (char)(hex(uniq[i]) << 4 | hex(uniq[i+1])); - } - -#undef hex - - tag->type = htons(TAG_HOST_UNIQ); - tag->length = htons(len / 2); - return 1; -} - -#define SET_STRING(var, val) do { if (var) free(var); var = strDup(val); } while(0); - -#define CHECK_ROOM(cursor, start, len) \ -do {\ - if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \ - error("Would create too-long packet"); \ - return; \ - } \ -} while(0) - -/* True if Ethernet address is broadcast or multicast */ -#define NOT_UNICAST(e) ((e[0] & 0x01) != 0) -#define BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) == 0xFF) -#define NOT_BROADCAST(e) ((e[0] & e[1] & e[2] & e[3] & e[4] & e[5]) != 0xFF) diff --git a/pppd/pppd.8 b/pppd/pppd.8 index 85af954..3b7c288 100644 --- a/pppd/pppd.8 +++ b/pppd/pppd.8 @@ -1204,7 +1204,7 @@ Use software flow control (i.e. XON/XOFF) to control the flow of data on the serial port. .SH PPPOE OPTIONS To establish PPP link over Ethernet (PPPoE) it is needed to load pppd's -\fBplugin rp-pppoe.so\fR and then specify option \fBnic-\fIinterface\fR +\fBplugin pppoe.so\fR and then specify option \fBnic-\fIinterface\fR instead of modem options \fIttyname\fR and \fIspeed\fR. Recognized pppd's PPPoE options are: .TP @@ -1215,24 +1215,30 @@ by specifying ppp'd option \fBnic-eth0\fR. Prefix \fBnic-\fR for this option may be avoided if interface name is unambiguous and does not look like any other pppd's option. .TP -.B rp_pppoe_service \fIname -Connect to specified PPPoE service name. +.B pppoe-service \fIname +Connect to specified PPPoE service name. For backward compatibility also +\fBrp_pppoe_service\fP option name is supported. .TP -.B rp_pppoe_ac \fIname -Connect to specified PPPoE access concentrator name. +.B pppoe-ac \fIname +Connect to specified PPPoE access concentrator name. For backward +compatibility also \fBrp_pppoe_ac\fP option name is supported. .TP -.B rp_pppoe_sess \fIsessid\fP:\fImacaddr -Attach to existing PPPoE session. +.B pppoe-sess \fIsessid\fP:\fImacaddr +Attach to existing PPPoE session. For backward compatibility also +\fBrp_pppoe_sess\fP option name is supported. .TP -.B rp_pppoe_verbose \fIn -Be verbose about discovered access concentrators. +.B pppoe-verbose \fIn +Be verbose about discovered access concentrators. For backward +compatibility also \fBrp_pppoe_verbose\fP option name is supported. .TP .B pppoe-mac \fImacaddr Connect to specified MAC address. .TP -.B host-uniq \fIstring +.B pppoe-host-uniq \fIstring Set the PPPoE Host-Uniq tag to the supplied hex string. By default PPPoE Host-Uniq tag is set to the pppd's process PID. +For backward compatibility this option may be specified without +\fBpppoe-\fP prefix. .TP .B pppoe-padi-timeout \fIn Initial timeout for discovery packets in seconds (default 5).