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