diff -u -N pppoe-0.2/Makefile pppoe-0.3/Makefile
--- pppoe-0.2/Makefile Fri Sep 24 16:32:35 1999
+++ pppoe-0.3/Makefile Sun Oct 24 15:10:53 1999
@@ -1,12 +1,27 @@
-#CFLAGS= -Wall -pedantic -ansi -g
-CFLAGS= -Wall -pedantic -ansi -O2
+#uncomment the following line to add checks for buggy ACs which send out
+#duplicate packets
+#DEFINES= -DBUGGY_AC
+#use the following line for OpenBSD support
+#DEFINES= -DUSE_BPF
+DEFINES=
-VERSION= 0.2
+#CFLAGS= -Wall -pedantic -ansi -g $(DEFINES)
+CFLAGS= -Wall -pedantic -ansi -O2 $(DEFINES)
+
+#Linux support doesn't need extra libraries, but OpenBSD support
+#does. If using OpenBSD, uncomment the following line:
+#LIBS=-lkvm
+
+
+VERSION= 0.3
pppoe: pppoe.o
- $(CC) -o pppoe pppoe.o
+ $(CC) -o pppoe pppoe.o $(LIBS)
pppoe.o: pppoe.c
distro:
- tar czvf pppoe-$(VERSION).tar.gz README LICENSE pppoe.c Makefile
+ rm -rf pppoe-$(VERSION)
+ mkdir pppoe-$(VERSION)
+ cp README LICENSE pppoe.c Makefile README.BSD start pppoe-$(VERSION)
+ tar czvf pppoe-$(VERSION).tar.gz pppoe-$(VERSION)/*
diff -u -N pppoe-0.2/README pppoe-0.3/README
--- pppoe-0.2/README Wed Sep 22 18:04:19 1999
+++ pppoe-0.3/README Sun Oct 24 15:10:53 1999
@@ -20,16 +20,31 @@
------------
Linux 2.0.0 or later (may work on other platforms, but untested)
+ has been tested on Intel platforms
+OR
+OpenBSD 2.5 or later (may work on other platforms, but untested)
+ has been tested on SparcStation IPX
pppd 2.3.7 or later
+Note that older versions of pppd may work if you obtain the
+'pty-redirect' program. Various copies are available on the 'Net. I
+haven't tried this myself, though.
+
Compiling
---------
Compile and install pppd. Then:
-Unpack and make:
+Unpack:
+
+# tar xzvf pppoe-0.3.tar.gz
+
+Edit the Makefile to set options. Currently, you can set one option
+which attempts to deal with buggy Access Concentrators that
+occasionally send out duplicate packets.
+
+Compile:
-# tar xzvf pppoe-0.1.tar.gz
# make
Install to some convenient directory:
@@ -50,12 +65,12 @@
Make sure your ethernet interface is up:
-ifconfig eth0 0.0.0.0 up
+ifconfig eth0 up
Then, start pppd like this:
pppd pty '/usr/local/sbin/pppoe -I eth0' noipdefault defaultroute \
- hide-password passive persist
+ hide-password passive persist name b1aaaaaa@sympatico.ca
Options
=======
@@ -70,9 +85,83 @@
Specifes a log file. Note that pppd chroots to '/', so the path
should be absolute. Note that the log can get large.
+-E file
+ Specifies an error log file. This is the file that diagnostic/error
+ messages go to. By default, it is stderr. Note that the path must
+ be absolute.
+
+-Fa
+ Specifies that partial packets should always be forwarded. Default
+ is no forwarding of partial packets.
+
+-Fs
+ Specifies that the program should search for start-of-packet data in
+ the data stream from pppd. Default is no forwarding of partial
+ packets.
+
-V
Prints the version number, and exits.
+Masquarading and Stuff
+======================
+
+It seems that a lot of people are using this software to run on small,
+cheap computers acting as firewalls or masquaraders for small
+networks. This section deals with some of the issues and problems
+relating to this.
+
+It appears that there is some kind of problem in the IP framgentation
+code in the Linux networking chain -- either at the kernel level, or
+in pppd. When a Linux router receives a IP packet on an interface and
+is asked to forward it to another interface which has an MTU smaller
+than the packet size, something goes wrong. pppd will spit out a
+packet which is MTU bytes in size, and then it will output the rest of
+the packet, BUT NOT ENCODED AS PER RFC1662. The net result of this is
+that pppoe will see this data, and will abort with an "invalid data"
+message. This behaviour has been modified in this version.
+
+If no '-F' option is given, these overflow packets are silently
+dropped. This will likely cause problems with data not getting where
+it's supposed to go; however, the connection should remain up.
+
+If a '-Fa' option is given, then whatever gibberish pppd outputs will
+be faithfully forwarded inside a PPPoE frame.
+
+If a '-Fs' option is given, then the pppd output stream is searched for
+the start of a valid RFC1662 packet, and that data is sent. Note that
+'-Fa' will over-ride '-Fs'.
+
+However, to avoid problems altogether, it is best to set the MTU on
+all machines behind the firewall. The MTUs should be set to about
+1400 or so. The way this is done is as follows:
+
+Under Linux (and, presumably other Unix-workalikes):
+
+# /sbin/ifconfig eth0 mtu 1400
+
+where eth0 is your Ethernet interface.
+
+Under Windows (95/98; may work under W2K):
+
+Using a registry editor, set
+
+HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Class\NetTrans\000X\
+MaxMTU=1492
+
+all on one line (note that the 1492 is a string value). Registry
+entries are case-sensitive.
+
+The 'X' depends on what your interface is.
+
+Under Windows NT (thanks to Shawn Sulma for this)
+
+Using a registry editor, set
+HKEY_LOCAL_MACHINE/System/CurrentControlSet/Services/El90x1/Parameters/
+Tcpip/MTU=1492
+
+(note that the 1492 is a DWORD)
+The "El90x1" is replaced with your card entry.
+
Bugs
====
@@ -86,8 +175,25 @@
this may have something to do with a bug in Access Concentrator used
by Bell, but I'm not sure.
+Acknowledgements
+================
+
+Thanks go to:
+
+Matt Robinson for the OpenBSD port and some major efficiency fixes.
+
+Dave Wortman for telling me how to port it to libc 5
+
+The teeming multitudes for testing and using this software, and
+providing feedback.
+
+And no thanks at all to Bell Nexxia and Sympatico, for using PPPoE in
+the first place. There *have* to be better solutions. And don't get
+me started on buggy RedBac hardware, either.
+
Author Information
==================
I can be reached via e-mail at . This program
can be found at http://www.ecf.toronto.edu/~stras/pppoe.html
+
diff -u -N pppoe-0.2/README.BSD pppoe-0.3/README.BSD
--- pppoe-0.2/README.BSD Thu Jan 1 00:00:00 1970
+++ pppoe-0.3/README.BSD Sun Oct 24 15:10:53 1999
@@ -0,0 +1,70 @@
+README.BSD
+
+This version of Luke Stras' PPPoE redirector includes support for OpenBSD.
+Support for other BSD variants is possible but requires some porting.
+To compile for BSD, you will need to add "-DUSE_BPF" in the DEFINES=
+string in the Makefile. You will also need to link against kvm library
+(uncomment LIBS=-lkvm in the Makefile).
+
+I am currently running this on a SPARCstation IPX running OpenBSD 2.5.
+I get 50Kbytes/sec maximum throughput but only if I saturate the
+machine with multiple connections to get around packet loss. [Under
+load BPF reports a number of lost packets.] At this point, the CPU
+on the machine is being fully used. For Intel users, the IPX is roughly
+on par with a fast 486 so anyone with a modern machine should be fine.
+
+NOTE: I have not run this on an Intel box yet, so there may be some
+byte-ordering issues [SPARCs use the same endian as network ordering].
+If you get the error message:
+
+ BPF program is broken
+
+after starting, try recompiling with -DSIMPLE_BPF. If this works,
+let me know - it means I've buggered up the byte-ordering in the
+more complex (and better performing) BPF program.
+
+---
+
+Look at the "start" script for an example of how to start this up.
+I found the pty-redir program in another package but that package
+did not credit it's author. It is GPL'ed so I have no qualms about
+redistributing it. I've hacked it slightly but otherwise make no
+claims on it. If the author will speak up I'll more than happily
+attribute it.
+
+Other than that, the instructions that came with the original package
+more or less apply. Note that the pppd in OpenBSD 2.5 does not support
+the "pty" option, hence the pty-redir program.
+
+---
+
+The BSD specific stuff is made up mostly of 2 chunks:
+
+1. Berkeley Packet Filter code which handles the reading and writing
+ of raw ethernet packets from and to the network.
+
+2. An ugly bit of kvm code to extract the ethernet address of the
+ interface we are interested in. This works on OpenBSD 2.5, may
+ work on other OpenBSD kernels and is unlikely to work anywhere else.
+ If it does work anywhere else or if anyone has any better ideas,
+ please let me know.
+
+Part 1 should work on any BSD variant or any platform supporting BPF.
+Part 2 will require the most porting work.
+
+Changes and comments about the BSD-specific parts are welcome at
+matt@cs.yorku.ca.
+
+Please note: this is not meant to be an elegant solution, just a stopgap
+until something better (i.e. something that doesn't dive in and out
+user-space and copy packets everywhere) can be worked out.
+
+Thanks to Luke for writing the Linux version which made the task of
+getting my gateway back online significantly easier.
+
+Matt Robinson
+Computer Development Manager
+Dept. of Computer Science
+York Univerity
+Toronto, Ontario, Canada
+matt@cs.yorku.ca
diff -u -N pppoe-0.2/pppoe.c pppoe-0.3/pppoe.c
--- pppoe-0.2/pppoe.c Fri Sep 24 16:30:46 1999
+++ pppoe-0.3/pppoe.c Sun Oct 24 15:10:53 1999
@@ -19,30 +19,62 @@
* Revision History
* 1999/09/22 stras Initial version
* 1999/09/24 stras Changed header files for greater portability
+ * 1999/10/02 stras Added more logging, bug fixes
+ * 1999/10/02 mr Port to bpf/OpenBSD; starvation fixed; efficiency fixes
+ * 1999/10/18 stras added BUGGY_AC code, partial forwarding
*/
-#include
+#include
+#include
+#include
#include
+#ifdef __linux__
#include
-#include
+#endif /* __linux__ */
+#if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ < 6
+#include
+#else
+#include
+#endif
#include
+#ifdef __linux__
#include
+#endif
#include
#include
#include
#include
#include
-#include
-#include
-#include
+#include
#include
+#include
+
+#ifdef USE_BPF
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif /* ETH_ALEN */
+/* set this to desired size - you may not get this */
+int bpf_buf_size = 65536;
+#include
+#include
+#include
+#include
+unsigned char local_ether[ETH_ALEN]; /* need to this filter packets */
+#endif /* USE_BPF */
+#include
+#ifdef __linux__
extern int errno;
+#endif
+
+/* used as the size for a packet buffer */
+/* should be > 2 * size of max packet size */
+#define PACKETBUF 4096
#define VERSION_MAJOR 0
-#define VERSION_MINOR 1
+#define VERSION_MINOR 3
/* references: RFC 2516 */
/* ETHER_TYPE fields for PPPoE */
@@ -55,7 +87,11 @@
/* PPPoE packet; includes Ethernet headers and such */
struct pppoe_packet {
+#ifdef __linux__
struct ethhdr ethhdr; /* ethernet header */
+#else
+ struct ether_header ethhdr; /* ethernet header */
+#endif
unsigned int ver:4; /* pppoe version */
unsigned int type:4; /* pppoe type */
unsigned int code:8; /* pppoe code CODE_* */
@@ -97,8 +133,20 @@
#define TAG_AC_SYSTEM_ERROR 0x0202
#define TAG_GENERIC_ERROR 0x0203
-int opt_verbose = 0;
+/* globals */
+int opt_verbose = 0; /* logging */
+int opt_fwd = 0; /* forward invalid packets */
+int opt_fwd_search = 0; /* search for next packet when forwarding */
FILE *log_file = NULL;
+FILE *error_file = NULL;
+
+pid_t sess_listen = 0, pppd_listen = 0; /* child processes */
+int disc_sock = 0, sess_sock = 0; /* PPPoE sockets */
+char src_addr[ETH_ALEN]; /* source hardware address */
+char dst_addr[ETH_ALEN]; /* destination hardware address */
+char *if_name = NULL; /* interface to use */
+int session = 0; /* identifier for our session */
+int clean_child = 0; /* flag set when SIGCHLD received */
void
print_hex(unsigned char *buf, int len)
@@ -119,23 +167,46 @@
{
int i;
struct pppoe_tag *t = (struct pppoe_tag*)(p + 1);
+ struct pppoe_tag tag; /* needed to avoid alignment problems */
char *buf;
-
+ time_t tm;
+
if (opt_verbose == 0)
- return;
-
+ return;
+
+ time(&tm);
+
fprintf(log_file, "Ethernet header:\n");
fprintf(log_file, "h_dest: ");
+#ifdef __linux__
for (i = 0; i < 6; i++)
fprintf(log_file, "%02x:", (unsigned)p->ethhdr.h_dest[i]);
-
+#else
+ for (i = 0; i < 6; i++)
+ fprintf(log_file, "%02x:", (unsigned)p->ethhdr.ether_dhost[i]);
+#endif
fprintf(log_file, "\nh_source: ");
+#ifdef __linux__
for (i = 0; i < 6; i++)
fprintf(log_file, "%02x:", (unsigned)p->ethhdr.h_source[i]);
+#else
+ for (i = 0; i < 6; i++)
+ fprintf(log_file, "%02x:", (unsigned)p->ethhdr.ether_shost[i]);
+#endif
+#ifdef __linux__
fprintf(log_file, "\nh_proto: 0x%04x ",
(unsigned)ntohs(p->ethhdr.h_proto));
+#else
+ fprintf(log_file, "\nh_proto: 0x%04x ",
+ (unsigned)ntohs(p->ethhdr.ether_type));
+#endif
+
+#ifdef __linux__
switch((unsigned)ntohs(p->ethhdr.h_proto))
+#else
+ switch((unsigned)ntohs(p->ethhdr.ether_type))
+#endif
{
case ETH_P_PPPOE_DISC:
fprintf(log_file, "(PPPOE Discovery)\n");
@@ -173,7 +244,11 @@
fprintf(log_file, "(Unknown)\n");
}
+#ifdef __linux__
if (ntohs(p->ethhdr.h_proto) != ETH_P_PPPOE_DISC)
+#else
+ if (ntohs(p->ethhdr.ether_type) != ETH_P_PPPOE_DISC)
+#endif
{
print_hex((unsigned char *)(p+1), ntohs(p->length));
return;
@@ -182,9 +257,11 @@
while (t < (struct pppoe_tag *)((char *)(p+1) + ntohs(p->length)))
{
+ /* no guarantee in PPPoE spec that t is aligned at all... */
+ memcpy(&tag,t,sizeof(tag));
fprintf(log_file, "PPPoE tag:\ntype: %04x length: %04x ",
- ntohs(t->type), ntohs(t->length));
- switch(ntohs(t->type))
+ ntohs(tag.type), ntohs(tag.length));
+ switch(ntohs(tag.type))
{
case TAG_END_OF_LIST:
fprintf(log_file, "(End of list)\n");
@@ -219,18 +296,18 @@
default:
fprintf(log_file, "(Unknown)\n");
}
- if (ntohs(t->length) > 0)
- switch (ntohs(t->type))
+ if (ntohs(tag.length) > 0)
+ switch (ntohs(tag.type))
{
case TAG_SERVICE_NAME:
case TAG_AC_NAME:
case TAG_SERVICE_NAME_ERROR:
case TAG_AC_SYSTEM_ERROR:
case TAG_GENERIC_ERROR: /* ascii data */
- buf = malloc(ntohs(t->length) + 1);
- memset(buf, 0, ntohs(t->length)+1);
- strncpy(buf, (char *)(t+1), ntohs(t->length));
- buf[ntohs(t->length)] = '\0';
+ buf = malloc(ntohs(tag.length) + 1);
+ memset(buf, 0, ntohs(tag.length)+1);
+ strncpy(buf, (char *)(t+1), ntohs(tag.length));
+ buf[ntohs(tag.length)] = '\0';
fprintf(log_file, "data (UTF-8): %s\n", buf);
free(buf);
break;
@@ -239,7 +316,7 @@
case TAG_AC_COOKIE:
case TAG_RELAY_SESSION_ID:
fprintf(log_file, "data (bin): ");
- for (i = 0; i < ntohs(t->length); i++)
+ for (i = 0; i < ntohs(tag.length); i++)
fprintf(log_file, "%02x", (unsigned)*((char *)(t+1) + i));
fprintf(log_file, "\n");
break;
@@ -247,14 +324,198 @@
default:
fprintf(log_file, "unrecognized data\n");
}
- t = (struct pppoe_tag *)((char *)(t+1)+ntohs(t->length));
+ t = (struct pppoe_tag *)((char *)(t+1)+ntohs(tag.length));
}
}
int
-create_raw_socket(unsigned short type)
+open_interface(char *if_name, unsigned short type, char *hw_addr)
{
+/* BSD stuff by mr */
+#ifdef USE_BPF
+ int fd;
+ struct ifreq ifr;
+ char bpf[16];
+ int i, opt;
+#ifdef SIMPLE_BPF
+ /* a simple BPF program which just grabs the packets of the given type */
+ /* by default use the clever BPF program - it works on my SPARC which
+ has the same endian as network order. If someone can confirm that
+ the ordering also works on the opposite ending (e.g. ix86) I'll
+ remove the simple filter BPF program for good */
+ struct bpf_insn filt[] = {
+ BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0 /* fill-in */, 0, 1), /* check type */
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+ BPF_STMT(BPF_RET+BPF_K, 0)
+ };
+#else
+ /* by default use the clever BPF program which filters out packets
+ originating from us in the kernel */
+ /* note that we split the 6-byte ethernet address into a 4-byte word
+ and 2-byte half-word to minimize the number of comparisons */
+ struct bpf_insn filt[] = {
+ BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0 /* fill-in */, 0, 5), /* check type */
+ /* check src address != our hw address */
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 6),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0 /* fill-in */, 0, 2), /* 4 bytes */
+ BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 10),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0 /* fill-in */, 1, 0), /* 2 bytes */
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+ BPF_STMT(BPF_RET+BPF_K, 0)
+ };
+#endif /* SIMPLE_BPF */
+ struct bpf_program prog;
+
+ /* hunt for an open bpf */
+ for(i = 0; i < 10; i++) { /* this max is arbitrary */
+ sprintf(bpf,"/dev/bpf%d",i);
+ if ((fd = open(bpf, O_RDWR)) >= 0)
+ break;
+ }
+ if (fd < 0) {
+ perror("pppoe: open(bpf)");
+ return -1;
+ }
+
+ /* try to increase BPF size if possible */
+ (void) ioctl(fd, BIOCSBLEN, &bpf_buf_size); /* try to set buffer size */
+ if (ioctl(fd, BIOCGBLEN, &bpf_buf_size) < 0) { /* but find out for sure */
+ perror("pppoe: bpf(BIOCGBLEN)");
+ return -1;
+ }
+
+
+ /* attach to given interface */
+ strncpy(ifr.ifr_name,if_name,sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, &ifr) < 0) {
+ perror("pppoe: bpf(BIOCSETIF)");
+ return -1;
+ }
+
+ /* setup BPF */
+ opt = 1;
+ if (ioctl(fd, BIOCIMMEDIATE, &opt) < 0) {
+ perror("pppoe: bpf(BIOCIMMEDIATE)");
+ return -1;
+ }
+ if (ioctl(fd, BIOCGDLT, &opt) < 0) {
+ perror("pppoe: bpf(BIOCGDLT)");
+ return -1;
+ }
+ if (opt != DLT_EN10MB) {
+ fprintf(stderr, "pppoe: interface %s is not Ethernet!\n", if_name);
+ return -1;
+ }
+
+ /*************************************************************************
+ *
+ * WARNING - Really non-portable stuff follows. This works on OpenBSD 2.5
+ * and may not work anywhere else.
+ *
+ * What's going on - there's no obvious user-level interface to determine
+ * the MAC address of a network interface in BSD that I know of. (If
+ * anyone has an idea, please let me know.) What happens here is that we
+ * dig around in the kernel symbol list to find its list of interfaces,
+ * walk through the list to find the interface we are interested in and
+ * then we can (inobviously) get the ethernet info from that.
+ * I don't like this solution, but it's the best I've got at this point.
+ *
+ *************************************************************************/
+
+ {
+ kvm_t *k;
+ struct nlist n[2];
+ struct ifnet_head ifhead;
+ struct ifnet intf;
+ unsigned long v;
+ char ifn[IFNAMSIZ+1];
+ struct arpcom arp;
+
+ k = kvm_open(NULL,NULL,NULL,O_RDONLY,"pppoe");
+ if (k == NULL) {
+ fprintf(stderr, "pppoe: failed to open kvm\n");
+ return -1;
+ }
+ n[0].n_name = "_ifnet";
+ n[1].n_name = NULL;
+ if (kvm_nlist(k,n) != 0) {
+ fprintf(stderr, "pppoe: could not find interface list\n");
+ kvm_close(k);
+ return -1;
+ }
+ if (kvm_read(k,n[0].n_value,(void *)&ifhead,sizeof(ifhead)) !=
+ sizeof(ifhead)) {
+ fprintf(stderr, "pppoe: could not read ifnet_head structure\n");
+ kvm_close(k);
+ return -1;
+ }
+ v = (unsigned long)(ifhead.tqh_first);
+ while(v != 0) {
+ if (kvm_read(k,v,(void *)&intf,sizeof(intf)) != sizeof(intf)) {
+ fprintf(stderr, "pppoe: could not read ifnet structure\n");
+ kvm_close(k);
+ return -1;
+ }
+ strncpy(ifn,intf.if_xname,IFNAMSIZ);
+ ifn[IFNAMSIZ] = '\0';
+ if (strcmp(ifn,if_name) == 0)
+ /* found our interface */
+ break;
+ else
+ /* walk the chain */
+ v = (unsigned long)(intf.if_list.tqe_next);
+ }
+ if (v == 0) {
+ fprintf(stderr, "pppoe: cannot find interface %s in kernel\n",if_name);
+ kvm_close(k);
+ return -1;
+ }
+ /* since we have the right interface, and we determined previously
+ that it is an ethernet interface, reread from the same address into
+ a "struct arpcom" structure (which begins with a struct ifnet).
+ The ethernet address is located past the end of the ifnet structure */
+ if (kvm_read(k,v,(void *)&arp,sizeof(arp)) != sizeof(arp)) {
+ fprintf(stderr, "could not read arpcom structure\n");
+ kvm_close(k);
+ return -1;
+ }
+ /* whew! */
+ /* save a copy of this for ourselves */
+ memcpy(local_ether,arp.ac_enaddr,ETH_ALEN);
+ if (hw_addr)
+ memcpy(hw_addr,arp.ac_enaddr,ETH_ALEN); /* also copy if requested */
+ kvm_close(k);
+ }
+
+ /* setup BPF filter */
+ {
+ union { unsigned int i; unsigned char b[4]; } x;
+ union { unsigned short i; unsigned char b[2]; } y;
+
+ filt[1].k = type; /* set type of packet we are looking for */
+#ifndef SIMPLE_BPF
+ /* now setup our source address so it gets filtered out */
+ for(i = 0; i < 4; i++)
+ x.b[i] = local_ether[i];
+ for(i = 0; i < 2; i++)
+ y.b[i] = local_ether[i+4];
+ filt[3].k = x.i;
+ filt[5].k = y.i;
+#endif /* SIMPLE_BPF */
+ }
+ prog.bf_insns = filt;
+ prog.bf_len = sizeof(filt)/sizeof(struct bpf_insn);
+ if (ioctl(fd, BIOCSETF, &prog) < 0) {
+ perror("pppoe: bpf(BIOCSETF)");
+ return -1;
+ }
+
+ return fd;
+#else /* do regular linux stuff */
int optval = 1, rv;
+ struct ifreq ifr;
if ((rv = socket(PF_INET, SOCK_PACKET, htons(type))) < 0)
{
@@ -268,75 +529,67 @@
return -1;
}
+ if (hw_addr != NULL) {
+ strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
+
+ if (ioctl(rv, SIOCGIFHWADDR, &ifr) < 0)
+ {
+ perror("pppoe: ioctl(SIOCGIFHWADDR)");
+ return -1;
+ }
+
+ if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
+ {
+ fprintf(error_file, "pppoe: interface %s is not Ethernet!\n", if_name);
+ return -1;
+ }
+
+ memcpy(hw_addr, ifr.ifr_hwaddr.sa_data, sizeof(ifr.ifr_hwaddr.sa_data));
+
+ }
return rv;
+#endif /* USE_BPF / linux */
}
int
-get_hw_addr(int s, char *if_name, char *hw_addr)
-{
- struct ifreq ifr;
-
- strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
-
- if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0)
- {
- perror("pppoe: ioctl(SIOCGIFHWADDR)");
- return -1;
- }
-
- if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
- {
- fprintf(stderr, "pppoe: interface %s is not Ethernet!\n", if_name);
- return -1;
- }
-
- memcpy(hw_addr, ifr.ifr_hwaddr.sa_data, sizeof(ifr.ifr_hwaddr.sa_data));
-
- return 0;
-}
-
-int
-create_padi(struct pppoe_packet **packet, const char *src, const char *name)
+create_padi(struct pppoe_packet *packet, const char *src, const char *name)
{
int size;
if (packet == NULL)
return 0;
- if (*packet != NULL)
- free(*packet);
-
size = sizeof(struct pppoe_packet) + sizeof(struct pppoe_tag);
if (name != NULL)
size += strlen(name);
- if ((*packet = malloc(size)) == NULL)
- {
- fprintf(stderr, "pppoe: malloc (create_padi)");
- return 0;
- }
-
- memcpy((*packet)->ethhdr.h_dest, MAC_BCAST_ADDR, 6);
- memcpy((*packet)->ethhdr.h_source, src, 6);
- (*packet)->ethhdr.h_proto = htons(ETH_P_PPPOE_DISC);
- (*packet)->ver = 1;
- (*packet)->type = 1;
- (*packet)->code = CODE_PADI;
- (*packet)->session = 0;
- (*packet)->length = htons(size - sizeof(struct pppoe_packet));
+#ifdef __linux__
+ memcpy(packet->ethhdr.h_dest, MAC_BCAST_ADDR, 6);
+ memcpy(packet->ethhdr.h_source, src, 6);
+ packet->ethhdr.h_proto = htons(ETH_P_PPPOE_DISC);
+#else
+ memcpy(packet->ethhdr.ether_dhost, MAC_BCAST_ADDR, 6);
+ memcpy(packet->ethhdr.ether_shost, src, 6);
+ packet->ethhdr.ether_type = htons(ETH_P_PPPOE_DISC);
+#endif
+ packet->ver = 1;
+ packet->type = 1;
+ packet->code = CODE_PADI;
+ packet->session = 0;
+ packet->length = htons(size - sizeof(struct pppoe_packet));
/* fill out a blank service-name tag */
- (*(struct pppoe_tag *)(*packet+1)).type = htons(TAG_SERVICE_NAME);
- (*(struct pppoe_tag *)(*packet+1)).length = name ? htons(strlen(name)) : 0;
+ (*(struct pppoe_tag *)(packet+1)).type = htons(TAG_SERVICE_NAME);
+ (*(struct pppoe_tag *)(packet+1)).length = name ? htons(strlen(name)) : 0;
if (name != NULL)
- memcpy((char *)(*packet + 1) + sizeof(struct pppoe_tag), name,
+ memcpy((char *)(packet + 1) + sizeof(struct pppoe_tag), name,
strlen(name));
return size;
}
int
-create_padr(struct pppoe_packet **packet, const char *src, const char *dst,
+create_padr(struct pppoe_packet *packet, const char *src, const char *dst,
char *name)
{
int size;
@@ -344,35 +597,32 @@
if (packet == NULL)
return 0;
- if (*packet != NULL)
- free(*packet);
-
size = sizeof(struct pppoe_packet) + sizeof(struct pppoe_tag);
if (name != NULL)
size += strlen(name);
- if ((*packet = malloc(size)) == NULL)
- {
- fprintf(stderr, "pppoe: malloc (create_padr)");
- return 0;
- }
-
- memcpy((*packet)->ethhdr.h_dest, dst, 6);
- memcpy((*packet)->ethhdr.h_source, src, 6);
- (*packet)->ethhdr.h_proto = htons(ETH_P_PPPOE_DISC);
- (*packet)->ver = 1;
- (*packet)->type = 1;
- (*packet)->code = CODE_PADR;
- (*packet)->session = 0;
- (*packet)->length = htons(size - sizeof(struct pppoe_packet));
+#ifdef __linux__
+ memcpy(packet->ethhdr.h_dest, dst, 6);
+ memcpy(packet->ethhdr.h_source, src, 6);
+ packet->ethhdr.h_proto = htons(ETH_P_PPPOE_DISC);
+#else
+ memcpy(packet->ethhdr.ether_dhost, dst, 6);
+ memcpy(packet->ethhdr.ether_shost, src, 6);
+ packet->ethhdr.ether_type = htons(ETH_P_PPPOE_DISC);
+#endif
+ packet->ver = 1;
+ packet->type = 1;
+ packet->code = CODE_PADR;
+ packet->session = 0;
+ packet->length = htons(size - sizeof(struct pppoe_packet));
/* fill out a blank service-name tag */
- (*(struct pppoe_tag *)(*packet+1)).type = htons(TAG_SERVICE_NAME);
- (*(struct pppoe_tag *)(*packet+1)).length = name ? htons(strlen(name)) : 0;
+ (*(struct pppoe_tag *)(packet+1)).type = htons(TAG_SERVICE_NAME);
+ (*(struct pppoe_tag *)(packet+1)).length = name ? htons(strlen(name)) : 0;
if (name != NULL)
- memcpy((char *)(*packet + 1) + sizeof(struct pppoe_tag), name,
+ memcpy((char *)(packet + 1) + sizeof(struct pppoe_tag), name,
strlen(name));
-
+ memset(((char *)packet) + size, 0, 14);
return size;
}
@@ -420,8 +670,9 @@
register unsigned char * cp,
register int len)
{
- assert(sizeof (unsigned short) == 2);
- assert(((unsigned short) -1) > 0);
+/* assert(sizeof (unsigned short) == 2);
+ assert(((unsigned short) -1) > 0); */
+
while (len--)
fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff];
@@ -434,117 +685,117 @@
#define FRAME_CTL 0x03
#define FRAME_ENC 0x20
+#define ADD_OUT(c) { *out++ = (c); n++; if (opt_verbose) fprintf(log_file, "%x ", (c)); }
+
void encode_ppp(int fd, unsigned char *buf, int len)
{
static int first = 0;
- int out_len = len + 4; /* 2*frame+addr+ctl+fcs */
- unsigned char *out = malloc(out_len);
- unsigned char *out2 = malloc(2 * out_len);
- int out2_ptr = 0;
- int out_ptr = 0;
- int i;
- int fcs;
- unsigned char fl = FRAME_FLAG;
- unsigned char esc = FRAME_ESC;
+ unsigned char out_buf[PACKETBUF];
+ unsigned char *out = out_buf;
+ unsigned char header[2], tail[2];
+ int i,n;
+ unsigned short fcs;
+ time_t tm;
+
+ header[0] = FRAME_ADDR;
+ header[1] = FRAME_CTL;
+ fcs = pppfcs16(PPPINITFCS16, header, 2);
+ fcs = pppfcs16(fcs, buf, len) ^ 0xffff;
+ tail[0] = fcs & 0x00ff;
+ tail[1] = (fcs >> 8) & 0x00ff;
- if (out == NULL)
+ if (opt_verbose)
{
- fprintf(stderr, "malloc\n");
- exit(1);
+ time(&tm);
+ fprintf(log_file, "%sWriting to pppd: \n", ctime(&tm));
}
- out[0] = FRAME_ADDR;
- out[1] = FRAME_CTL;
- memcpy(out+2, buf, len);
- out_ptr = len+2;
- fcs = pppfcs16(PPPINITFCS16, out, len+2) ^ 0xffff;
- out[out_ptr++] = fcs & 0x00ff;
- out[out_ptr++] = (fcs >> 8) & 0x00ff;
-
- if (opt_verbose)
- fprintf(log_file, "Writing: \n");
- if (first == 0)
- {
- out2[out2_ptr] = fl;
- if (opt_verbose)
- fprintf(log_file, "%x ", (unsigned char)fl);
+ n = 0;
+ if (!first) {
+ ADD_OUT(FRAME_FLAG);
first = 1;
}
- for (i = 0; i < out_ptr; i++)
- if (out[i] == FRAME_FLAG || out[i] == FRAME_ESC || out[i] < 0x20)
+ ADD_OUT(FRAME_ADDR); /* the header - which is constant */
+ ADD_OUT(FRAME_ESC);
+ ADD_OUT(FRAME_CTL ^ FRAME_ENC);
+
+ for (i = 0; i < len; i++)
+ if (buf[i] == FRAME_FLAG || buf[i] == FRAME_ESC || buf[i] < 0x20)
{
- out2[out2_ptr++] = esc;
- out[i] ^= FRAME_ENC;
- out2[out2_ptr++] = *(out+i);
- if (opt_verbose)
- fprintf(log_file, "%x %x", (unsigned char)esc,
- (unsigned char)*(out+i));
+ ADD_OUT(FRAME_ESC);
+ ADD_OUT(buf[i] ^ FRAME_ENC);
}
else
- {
- out2[out2_ptr++] = *(out+i);
- if (opt_verbose)
- fprintf(log_file, "%x ", (unsigned char)*(out+i));
- }
+ ADD_OUT(buf[i]);
+
+ for (i = 0; i < 2; i++) {
+ if (tail[i] == FRAME_FLAG || tail[i] == FRAME_ESC || tail[i] < 0x20) {
+ ADD_OUT(FRAME_ESC);
+ ADD_OUT(tail[i] ^ FRAME_ENC);
+ } else
+ ADD_OUT(tail[i]);
+ }
+ ADD_OUT(FRAME_FLAG);
+
+ write(fd, out_buf, n);
- out2[out2_ptr++] = fl;
- write(fd, out2, out2_ptr);
if (opt_verbose)
- fprintf(log_file, "%x\n", fl);
- free(out);
- free(out2);
+ fprintf(log_file, "\n");
}
int
-create_sess(struct pppoe_packet **packet, const char *src, const char *dst,
+create_sess(struct pppoe_packet *packet, const char *src, const char *dst,
unsigned char *buf, int bufsize, int sess)
{
int size;
int i, o = 0;
- /* we assume a "proper" rfc 1662 packet here; this should be changed
- to do general decoding */
-
- if (!((buf[0] == FRAME_FLAG) || (buf[0] == FRAME_ADDR)))
+ if (opt_fwd || !((buf[0] == FRAME_FLAG) || (buf[0] == FRAME_ADDR)))
{
- fprintf(stderr, "create_sess: invalid data\n");
- exit(1);
+ if (opt_fwd_search) /* search for a valid packet */
+ {
+ while (*buf++ != FRAME_FLAG && bufsize != 0)
+ bufsize--;
+ if (bufsize == 0)
+ return 0;
+ }
+ else
+ {
+ fprintf(error_file, "create_sess: invalid data\n");
+ return 0;
+ }
}
- for (i = (buf[0] == FRAME_FLAG) ? 4 : 3; i < bufsize - 3; i++)
-/* skip fcs and trailing flag */
+ for (i = (buf[0] == FRAME_FLAG ? 4 : 3); i < bufsize - 1; i++)
if (buf[i] == FRAME_ESC)
buf[o++] = buf[++i] ^ FRAME_ENC;
else
buf[o++] = buf[i];
- bufsize = o;
+ bufsize = o - 2; /* ignore fcs */
if (packet == NULL)
return 0;
- if (*packet != NULL)
- free(*packet);
-
size = sizeof(struct pppoe_packet) + bufsize;
- if ((*packet = malloc(size)) == NULL)
- {
- fprintf(stderr, "pppoe: malloc (create_sess)");
- return 0;
- }
-
- memcpy((*packet)->ethhdr.h_dest, dst, 6);
- memcpy((*packet)->ethhdr.h_source, src, 6);
- (*packet)->ethhdr.h_proto = htons(ETH_P_PPPOE_SESS);
- (*packet)->ver = 1;
- (*packet)->type = 1;
- (*packet)->code = CODE_SESS;
- (*packet)->session = sess;
- (*packet)->length = htons(size - sizeof(struct pppoe_packet));
+#ifdef __linux__
+ memcpy(packet->ethhdr.h_dest, dst, 6);
+ memcpy(packet->ethhdr.h_source, src, 6);
+ packet->ethhdr.h_proto = htons(ETH_P_PPPOE_SESS);
+#else
+ memcpy(packet->ethhdr.ether_dhost, dst, 6);
+ memcpy(packet->ethhdr.ether_shost, src, 6);
+ packet->ethhdr.ether_type = htons(ETH_P_PPPOE_SESS);
+#endif
+ packet->ver = 1;
+ packet->type = 1;
+ packet->code = CODE_SESS;
+ packet->session = sess;
+ packet->length = htons(size - sizeof(struct pppoe_packet));
/* fill out payload */
- memcpy(*packet + 1, buf, bufsize);
+ memcpy(packet + 1, buf, bufsize);
return size;
}
@@ -552,16 +803,25 @@
int
send_packet(int sock, struct pppoe_packet *packet, int len, const char *ifn)
{
+#ifdef USE_BPF
+ int c;
+ if ((c = write(sock,packet,len)) != len)
+ perror("pppoe: write (send_packet)");
+ return c;
+#else /* regular linux stuff */
struct sockaddr addr;
int c;
+ time_t tm;
memset(&addr, 0, sizeof(addr));
strcpy(addr.sa_data, ifn);
if (opt_verbose == 1)
{
- fprintf(log_file, "Sending ");
+ time(&tm);
+ fprintf(log_file, "%sSending ", ctime(&tm));
print_packet(packet);
+ fputc('\n', log_file);
}
@@ -569,93 +829,297 @@
perror("pppoe: sendto (send_packet)");
return c;
+#endif /* USE_BPF */
}
-int wait_for_packet(int *sock, int ns, struct pppoe_packet **packet, int *len)
-{
- fd_set fdset;
- int i, maxs = -1;
- struct sockaddr_in from;
- socklen_t fromlen;
+#ifdef USE_BPF
+/* return: -1 == error, 0 == okay, 1 == ignore this packet */
+int read_bpf_packet(int fd, struct pppoe_packet *packet) {
+ /* Nastiness - BPF may return multiple packets in one fell swoop */
+ /* This makes select() difficult to use - you need to be ready to
+ clear out packets as they arrive */
+ static char *buf = NULL;
+ static int lastdrop = 0;
+ static int n = 0, off = 0;
+ struct bpf_hdr *h;
+
+ if (buf == NULL) {
+ if ((buf = malloc(bpf_buf_size)) == NULL) {
+ perror("pppoe:malloc");
+ return -1;
+ }
+ }
- while (1)
- {
- FD_ZERO(&fdset);
- for (i = 0; i < ns; i++)
- {
- FD_SET(sock[i], &fdset);
- if (sock[i] > maxs)
- maxs = sock[i];
+ if (off < n) {
+ /* read out of previously grabbed buffer */
+ if (n-off < sizeof(struct bpf_hdr)) {
+ fprintf(stderr, "BPF: not enough left for header: %d\n", n-off);
+ off = n = 0; /* force reread from BPF next time */
+ return 1; /* try again */
}
-
- if (select(maxs + 1, &fdset, NULL, NULL, NULL) < 0)
- {
- perror("pppoe: select (wait_for_packet)");
+ h = (struct bpf_hdr *)&(buf[off]);
+ memcpy(packet,&(buf[off + h->bh_hdrlen]),h->bh_caplen);
+ off += BPF_WORDALIGN(h->bh_hdrlen + h->bh_caplen);
+ if (h->bh_caplen != h->bh_datalen) {
+ fprintf(stderr, "pppoe: truncated packet: %d -> %d\n",
+ h->bh_datalen, h->bh_caplen);
+ return 1; /* try again */
+ }
+ } else {
+ struct bpf_stat s;
+ if (ioctl(fd,BIOCGSTATS,&s)) {
+ perror("pppoe: BIOCGSTATS");
+ } else {
+ if (s.bs_drop > lastdrop) {
+ fprintf(stderr, "BPF: dropped %d packets\n", s.bs_drop - lastdrop);
+ lastdrop = s.bs_drop;
+ }
+ }
+ if ((n = read(fd,buf,bpf_buf_size)) < 0) {
+ perror("pppoe: read (read_bpf_packet)");
return -1;
}
-
- for (i = 0; i < ns; i++)
- {
- if (FD_ISSET(sock[i], &fdset))
- {
- if (sock[i] < 3) /* ie, it's an fd */
- return sock[i];
+ if (n == 0)
+ return 0; /* timeout on bpf - try again */
+ h = (struct bpf_hdr *)(buf);
+ memcpy(packet,&(buf[h->bh_hdrlen]),h->bh_caplen);
+ off = BPF_WORDALIGN(h->bh_hdrlen + h->bh_caplen);
+ }
+ /* need to filter packets here - interface could be in promiscuous
+ mode - we shouldn't see packets that we sent out thanks to BPF, but
+ a quick double-check here is unlikely to seriously impact performance
+ Once you know BPF is working, you can pop this out */
+ if (memcmp(packet->ethhdr.ether_shost,local_ether,6) == 0) {
+#ifdef SIMPLE_BPF
+ return 1; /* ignore this packet */
+#else
+ /* with the bigger BPF program, we should never get here */
+ fprintf(stderr, "BPF program is broken\n");
+ exit(1);
+#endif /* SIMPLE_BPF */
+ }
+
+ if (memcmp(packet->ethhdr.ether_dhost,MAC_BCAST_ADDR,6) == 0 ||
+ memcmp(packet->ethhdr.ether_dhost,local_ether,6) == 0)
+ return 0; /* I should look at this packet */
+ else {
+ print_packet(packet);
+ return 1; /* ignore this packet */
+ }
+}
- if (*packet != NULL)
- free(*packet);
-
- if ((*packet = malloc(65536)) == NULL)
- {
- fprintf(stderr, "pppoe: malloc (wait_for_packet");
- return -1;
- }
-
- if (recvfrom(sock[i], *packet, 65536, 0,
- (struct sockaddr *)&from, &fromlen) < 0)
- {
- perror("pppoe: recv (wait_for_packet)");
- return -1;
- }
+int is_bpf(int fd) {
+ /* is this socket tied to bpf? */
+ /* quick hack() - try a trivial bpf ioctl */
+ struct bpf_version v;
+ return (ioctl(fd,BIOCVERSION,&v) == 0);
+}
+#endif /* USE_BPF */
- print_packet(*packet);
+int
+read_packet(int sock, struct pppoe_packet *packet, int *len)
+{
+/* struct sockaddr_in from; */
+#if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ < 6
+ int fromlen = PACKETBUF;
+#else
+ socklen_t fromlen = PACKETBUF;
+#endif
+ time_t tm;
- return sock[i];
- }
+ time(&tm);
+
+ while(1) {
+#ifdef USE_BPF
+ {
+ int j;
+ if ((j = read_bpf_packet(sock, packet)) < 0)
+ return -1; /* read_bpf_packet() will report error */
+ else if (j > 0)
+ continue; /* read a packet, but not what we wanted */
+ }
+#else
+ if (recvfrom(sock, packet, PACKETBUF, 0,
+ NULL /*(struct sockaddr *)&from*/, &fromlen) < 0) {
+ perror("pppoe: recv (read_packet)");
+ return -1;
}
+#endif /* USE_BPF */
+ if (opt_verbose)
+ {
+ fprintf(log_file, "Received packet at %s", ctime(&tm));
+ print_packet(packet);
+ fputc('\n', log_file);
+ }
+
+ return sock;
}
}
+void sigchild(int src) {
+ clean_child = 1;
+}
+
+void cleanup_and_exit(int status) {
+ close(disc_sock);
+ close(sess_sock);
+ close(1);
+ if (pppd_listen > 0)
+#ifdef __linux__
+ kill(pppd_listen, SIGTERM);
+#else
+ kill(SIGTERM, pppd_listen);
+#endif
+ if (sess_listen > 0)
+#ifdef __linux__
+ kill(sess_listen, SIGTERM);
+#else
+ kill(SIGTERM, sess_listen);
+#endif
+ exit(status);
+}
+
void sigint(int src)
{
- exit(1);
+ cleanup_and_exit(1);
+}
+
+
+void sess_handler(void) {
+ /* pull packets of sess_sock and feed to pppd */
+ struct pppoe_packet *packet = NULL;
+ int pkt_size;
+
+#ifdef BUGGY_AC
+/* the following code deals with buggy AC software which sometimes sends
+ duplicate packets */
+#define DUP_COUNT 10
+#define DUP_LENGTH 20
+ unsigned char dup_check[DUP_COUNT][DUP_LENGTH];
+ int i, ptr = 0;
+#endif /* BUGGY_AC */
+
+#ifdef BUGGY_AC
+ memset(dup_check, 0, sizeof(dup_check));
+#endif
+
+ /* allocate packet once */
+ packet = malloc(PACKETBUF);
+ assert(packet != NULL);
+
+ fprintf(error_file, "sess_handler %d\n", getpid());
+ while(1)
+ {
+ while(read_packet(sess_sock,packet,&pkt_size) != sess_sock)
+ ;
+#ifdef __linux__
+ if (memcmp(packet->ethhdr.h_source, dst_addr, sizeof(dst_addr)) != 0)
+#else
+ if (memcmp(packet->ethhdr.ether_shost, dst_addr, sizeof(dst_addr))
+ != 0)
+#endif
+ continue; /* packet not from AC */
+ if (packet->session != session)
+ continue; /* discard other sessions */
+#ifdef __linux__
+ if (packet->ethhdr.h_proto != htons(ETH_P_PPPOE_SESS))
+ {
+ fprintf(log_file, "pppoe: invalid session proto %x detected\n",
+ ntohs(packet->ethhdr.h_proto));
+ continue;
+ }
+#else
+ if (packet->ethhdr.ether_type != htons(ETH_P_PPPOE_SESS))
+ {
+ fprintf(log_file, "pppoe: invalid session proto %x detected\n",
+ ntohs(packet->ethhdr.ether_type));
+ continue;
+ }
+#endif
+ if (packet->code != CODE_SESS) {
+ fprintf(log_file, "pppoe: invalid session code %x\n", packet->code);
+ continue;
+ }
+#if BUGGY_AC
+ /* we need to go through a list of recently-received packets to
+ make sure the AC hasn't sent us a duplicate */
+ for (i = 0; i < DUP_COUNT; i++)
+ if (memcmp(packet, dup_check[i], sizeof(dup_check[0])) == 0)
+ return; /* we've received a dup packet */
+#define min(a,b) ((a) < (b) ? (a) : (b))
+ memcpy(dup_check[ptr], packet, min(ntohs(packet->length),
+ sizeof(dup_check[0])));
+ ptr = ++ptr % DUP_COUNT;
+#endif /* BUGGY_AC */
+ encode_ppp(1, (unsigned char *)(packet+1), ntohs(packet->length));
+ }
+}
+
+void pppd_handler(void) {
+ /* take packets from pppd and feed them to sess_sock */
+ struct pppoe_packet *packet = NULL;
+ unsigned char buf[PACKETBUF];
+ int len, pkt_size;
+ time_t tm;
+
+ fprintf(error_file, "pppd_handler %d\n", getpid());
+
+ /* allocate packet once */
+ packet = malloc(PACKETBUF);
+ assert(packet != NULL);
+
+ while(1) {
+ if ((len = read(0, buf, sizeof(buf))) < 0) {
+ perror("pppoe");
+ exit(1);
+ }
+ if (len == 0)
+ continue;
+
+ if (opt_verbose == 1) {
+ time(&tm);
+ fprintf(log_file, "\n%sInput of %d bytes:\n", ctime(&tm), len);
+ print_hex(buf, len);
+ fputc('\n', log_file);
+ }
+
+ if ((pkt_size = create_sess(packet, src_addr, dst_addr, buf, len,
+ session)) == 0) {
+ fprintf(error_file, "pppoe: unable to create packet\n");
+ continue;
+ }
+
+ if (send_packet(sess_sock, packet, pkt_size, if_name) < 0) {
+ fprintf(error_file, "pppoe: unable to send PPPoE packet\n");
+ exit(1);
+ }
+ }
}
int main(int argc, char **argv)
{
- char src_addr[ETH_ALEN]; /* source hardware address */
- char dst_addr[ETH_ALEN]; /* destination hardware address */
- char *if_name = NULL; /* interface to use */
-
- int disc_sock = 0, sess_sock = 0, socks[3]; /* raw socket we use */
- int in_sock;
-
struct pppoe_packet *packet = NULL;
int pkt_size;
- int state = CODE_PADI;
- int session = 0;
-
- unsigned char buf[MAX_PAYLOAD];
- int bufsize;
+ int opt;
- char opt;
+ /* initialize error_file here to avoid glibc2.1 issues */
+ error_file = stderr;
/* parse options */
-
- while ((opt = getopt(argc, argv, "I:L:V")) != -1)
+ while ((opt = getopt(argc, argv, "I:L:VE:F:")) != -1)
switch(opt)
{
+ case 'F': /* sets invalid forwarding */
+ if (*optarg == 'a') /* always forward */
+ opt_fwd = 1;
+ else if (*optarg == 's') /* search for flag */
+ opt_fwd_search = 1;
+ else
+ fprintf(stderr, "Invalid forward option %c\n", *optarg);
+ break;
+
case 'I': /* sets interface */
if (if_name != NULL)
free(if_name);
@@ -676,11 +1140,28 @@
fprintf(stderr, "fopen\n");
exit(1);
}
+ if (setvbuf(log_file, NULL, _IONBF, 0) != 0)
+ {
+ fprintf(stderr, "setvbuf\n");
+ exit(1);
+ }
break;
case 'V': /* version */
printf("pppoe version %d.%d\n", VERSION_MAJOR, VERSION_MINOR);
exit(0);
break;
+ case 'E': /* error file */
+ if ((error_file = fopen(optarg, "w")) == NULL)
+ {
+ fprintf(stderr, "fopen\n");
+ exit(1);
+ }
+ if (setvbuf(error_file, NULL, _IONBF, 0) != 0)
+ {
+ fprintf(stderr, "setvbuf\n");
+ exit(1);
+ }
+ break;
default:
fprintf(stderr, "Unknown option %c\n", optopt);
exit(1);
@@ -688,191 +1169,116 @@
if (if_name == 0)
if_name = "eth0";
-
+ /* allocate packet once */
+ packet = malloc(PACKETBUF);
+ assert(packet != NULL);
+
/* create the raw socket we need */
signal(SIGINT, sigint);
signal(SIGTERM, sigint);
- if ((disc_sock = create_raw_socket(ETH_P_PPPOE_DISC)) < 0)
+ if ((disc_sock = open_interface(if_name,ETH_P_PPPOE_DISC,src_addr)) < 0)
{
- fprintf(stderr, "pppoe: unable to create raw socket\n");
+ fprintf(error_file, "pppoe: unable to create raw socket\n");
return 1;
}
+
+ /* initiate connection */
+
+ /* start the PPPoE session */
+ if ((pkt_size = create_padi(packet, src_addr, NULL)) == 0) {
+ fprintf(stderr, "pppoe: unable to create PADI packet\n");
+ exit(1);
+ }
+ /* send the PADI packet */
+ if (send_packet(disc_sock, packet, pkt_size, if_name) < 0) {
+ fprintf(stderr, "pppoe: unable to send PADI packet\n");
+ exit(1);
+ }
- if (get_hw_addr(disc_sock, if_name, src_addr) != 0)
- {
- fprintf(stderr, "pppoe: unable to get hardware address\n");
- return 1;
+ /* wait for PADO */
+ while (read_packet(disc_sock, packet, &pkt_size) != disc_sock ||
+ (packet->code != CODE_PADO && packet->code != CODE_PADT)) {
+ fprintf(log_file, "pppoe: unexpected packet %x\n",
+ packet->code);
+ continue;
}
- /* main loop */
- while (1)
+#ifdef __linux__
+ memcpy(dst_addr, packet->ethhdr.h_source, sizeof(dst_addr));
+#else
+ memcpy(dst_addr, packet->ethhdr.ether_shost, sizeof(dst_addr));
+#endif
+
+ /* send PADR */
+ if ((pkt_size = create_padr(packet, src_addr, dst_addr, NULL)) == 0) {
+ fprintf(stderr, "pppoe: unable to create PADR packet\n");
+ exit(1);
+ }
+ if (send_packet(disc_sock, packet, pkt_size+14, if_name) < 0) {
+ fprintf(stderr, "pppoe: unable to send PADR packet\n");
+ exit(1);
+ }
+
+ /* wait for PADS */
+#ifdef __linux__
+ while (read_packet(disc_sock, packet, &pkt_size) != disc_sock ||
+ (memcmp(packet->ethhdr.h_source,
+ dst_addr, sizeof(dst_addr)) != 0))
+#else
+ while (read_packet(disc_sock, packet, &pkt_size) != disc_sock ||
+ (memcmp(packet->ethhdr.ether_shost,
+ dst_addr, sizeof(dst_addr)) != 0))
+#endif
{
- switch (state)
- {
- case CODE_PADI: /* initiate connection */
- /* start the PPPoE session */
- if ((pkt_size = create_padi(&packet, src_addr, NULL)) == 0)
- {
- fprintf(stderr, "pppoe: unable to create PADI packet\n");
- exit(1);
- }
- /* send the PADI packet */
- if (send_packet(disc_sock, packet, pkt_size, if_name) < 0)
- {
- fprintf(stderr, "pppoe: unable to send PADI packet\n");
- exit(1);
- }
-
- state = CODE_PADO;
- break;
-
- case CODE_PADO: /* wait for PADO */
- if (wait_for_packet(&disc_sock, 1, &packet, &pkt_size)
- != disc_sock || (packet->code != CODE_PADO &&
- packet->code != CODE_PADT))
- {
- fprintf(stderr, "pppoe: unexpected packet %x\n",
- packet->code);
- continue;
- }
- if (packet->code == CODE_PADT) /* early termination */
- {
- state = CODE_PADT;
- continue;
- }
- memcpy(dst_addr, packet->ethhdr.h_source, sizeof(dst_addr));
-
- state = CODE_PADR;
- break;
-
- case CODE_PADR: /* send PADR */
- if ((pkt_size = create_padr(&packet, src_addr, dst_addr, NULL))
- == 0)
- {
- fprintf(stderr, "pppoe: unable to create PADR packet\n");
- exit(1);
- }
- if (send_packet(disc_sock, packet, pkt_size, if_name) < 0)
- {
- fprintf(stderr, "pppoe: unable to send PADR packet\n");
- exit(1);
- }
- state = CODE_PADS;
- break;
-
- case CODE_PADS: /* wait for PADS */
- if (wait_for_packet(&disc_sock, 1, &packet, &pkt_size)
- != disc_sock || (packet->code != CODE_PADS &&
- packet->code != CODE_PADT))
- {
- fprintf(stderr, "pppoe: unexpected packet %x\n",
- packet->code);
- continue;
- }
- if (memcmp(packet->ethhdr.h_source, dst_addr, sizeof(dst_addr))
- != 0)
- continue; /* discard packets not from AC */
- if (packet->code == CODE_PADT) /* early termination */
- {
- state = CODE_PADT;
- continue;
- }
- session = packet->session;
- if ((sess_sock = create_raw_socket(ETH_P_PPPOE_SESS)) < 0)
- {
- fprintf(stderr, "pppoe: unable to create raw socket\n");
- return 1;
- }
- socks[0] = disc_sock;
- socks[1] = sess_sock;
- socks[2] = 0; /* stdin */
-
- state = STATE_RUN;
-
- break;
-
- case STATE_RUN: /* running */
-
- in_sock = wait_for_packet(socks, 3, &packet, &pkt_size);
- if (in_sock == disc_sock)
- {
- if (packet->code == CODE_PADT)
- state = CODE_PADT;
- else
- fprintf(stderr, "pppoe: unexpected packet %x\n",
- packet->code);
- continue;
- }
- else if (in_sock == sess_sock)
- {
- if (memcmp(packet->ethhdr.h_source, dst_addr, sizeof(dst_addr))
- != 0)
- continue; /* packet not from AC */
- if (packet->session != session)
- continue; /* discard other sessions */
- if (packet->ethhdr.h_proto != htons(ETH_P_PPPOE_SESS))
- {
- fprintf(stderr,
- "pppoe: invalid session proto %x detected\n",
- ntohs(packet->ethhdr.h_proto));
- continue;
- }
- if (packet->code != CODE_SESS)
- {
- fprintf(stderr, "pppoe: invalid session code %x\n",
- packet->code);
- continue;
- }
- encode_ppp(1, (unsigned char *)(packet+1),
- ntohs(packet->length));
- }
- else /* in_sock == stdin */
- {
- if ((bufsize = read(0, buf, sizeof(buf))) < 0)
- {
- perror("pppoe");
- exit(1);
- }
- if (bufsize == 0)
- continue;
-
- if (opt_verbose == 1)
- {
- fprintf(log_file, "Input of %d bytes:\n", bufsize);
- print_hex(buf, bufsize);
- fflush(log_file);
- }
-
- if ((pkt_size =
- create_sess(&packet, src_addr, dst_addr, buf,
- bufsize, session)) == 0)
- {
- fprintf(stderr, "pppoe: unable to create packet\n");
- exit(1);
- }
- if (send_packet(sess_sock, packet, pkt_size, if_name) < 0)
- {
- fprintf(stderr, "pppoe: unable to send PPPoE packet\n");
- exit(1);
- }
- }
- break;
-
- case CODE_PADT: /* received a PADT */
- close(disc_sock);
- close(sess_sock);
- close(1);
- exit(0);
- break;
+ if (packet->code != CODE_PADS && packet->code != CODE_PADT)
+ fprintf(log_file, "pppoe: unexpected packet %x\n", packet->code);
+ continue;
+ }
- default:
- fprintf(stderr, "pppoe: invalid state %x", state);
- exit(1);
+ if (packet->code == CODE_PADT) /* early termination */
+ cleanup_and_exit(0);
+
+ session = packet->session;
+ if ((sess_sock = open_interface(if_name,ETH_P_PPPOE_SESS,NULL)) < 0) {
+ fprintf(log_file, "pppoe: unable to create raw socket\n");
+ cleanup_and_exit(1);
+ }
+
+ clean_child = 0;
+ signal(SIGCHLD, sigchild);
+
+ /* all sockets are open fork off handlers */
+ if ((sess_listen = fork()) == 0)
+ sess_handler(); /* child */
+ if (sess_listen < 0) {
+ perror("pppoe: fork");
+ cleanup_and_exit(1);
+ }
+ if ((pppd_listen = fork()) == 0)
+ pppd_handler(); /* child */
+ if (pppd_listen < 0) {
+ perror("pppoe: fork");
+ cleanup_and_exit(1);
+ }
+
+
+ /* wait for all children to die */
+ /* this is not perfect - race conditions on dying children are still
+ possible */
+ while(1) {
+ if (waitpid((pid_t)-1,NULL,WNOHANG) < 0 && errno == ECHILD)
+ break; /* all children dead */
+ if (read_packet(disc_sock, packet, &pkt_size) == disc_sock) {
+ if (packet->code == CODE_PADT)
+ cleanup_and_exit(1);
}
+ /* clean up any dead children */
+ while (waitpid((pid_t)-1,NULL,WNOHANG) > 0)
+ ;
+
}
return 0;
-
}
diff -u -N pppoe-0.2/start pppoe-0.3/start
--- pppoe-0.2/start Thu Jan 1 00:00:00 1970
+++ pppoe-0.3/start Sun Oct 24 15:10:53 1999
@@ -0,0 +1,8 @@
+#!/bin/sh -x
+USER=b1xxxxxx@sympatico.ca
+pty=`./pty-redir ./pppoe -I le0`
+if [ "X$pty" = "X" ]; then
+ echo >&2 "cannot start pppoe"
+ exit 1
+fi
+/usr/sbin/pppd $pty noipdefault defaultroute passive persist name $USER lcp-echo-interval 30