📄 pppoe.c
字号:
/* * pppoe, a PPP-over-Ethernet redirector * Copyright (C) 1999 Luke Stras <stras@ecf.toronto.edu> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * 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 <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <net/if.h>#ifdef __linux__#include <net/if_arp.h>#endif /* __linux__ */#if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ < 6#include <linux/if_ether>#else#include <netinet/if_ether.h>#endif#include <assert.h> #ifdef __linux__#include <getopt.h>#endif#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/time.h>#include <sys/ioctl.h>#include <sys/wait.h>#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 <net/bpf.h>#include <fcntl.h>#include <nlist.h>#include <kvm.h>unsigned char local_ether[ETH_ALEN]; /* need to this filter packets */#endif /* USE_BPF */#include <errno.h>#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 3/* references: RFC 2516 *//* ETHER_TYPE fields for PPPoE */#define ETH_P_PPPOE_DISC 0x8863 /* discovery stage */#define ETH_P_PPPOE_SESS 0x8864 /* session stage *//* ethernet broadcast address */#define MAC_BCAST_ADDR "\xff\xff\xff\xff\xff\xff"/* 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_* */ unsigned int session:16; /* session id */ unsigned short length; /* payload length */ /* payload follows */};/* maximum payload length */#define MAX_PAYLOAD (1484 - sizeof(struct pppoe_packet))/* PPPoE codes */#define CODE_SESS 0x00 /* PPPoE session */#define CODE_PADI 0x09 /* PPPoE Active Discovery Initiation */#define CODE_PADO 0x07 /* PPPoE Active Discovery Offer */#define CODE_PADR 0x19 /* PPPoE Active Discovery Request */#define CODE_PADS 0x65 /* PPPoE Active Discovery Session-confirmation */#define CODE_PADT 0xa7 /* PPPoE Active Discovery Terminate *//* also need */#define STATE_RUN (-1)/* PPPoE tag; the payload is a sequence of these */struct pppoe_tag { unsigned short type; /* tag type TAG_* */ unsigned short length; /* tag length */ /* payload follows */};/* PPPoE tag types */#define TAG_END_OF_LIST 0x0000#define TAG_SERVICE_NAME 0x0101#define TAG_AC_NAME 0x0102#define TAG_HOST_UNIQ 0x0103#define TAG_AC_COOKIE 0x0104#define TAG_VENDOR_SPECIFIC 0x0105#define TAG_RELAY_SESSION_ID 0x0110#define TAG_SERVICE_NAME_ERROR 0x0201#define TAG_AC_SYSTEM_ERROR 0x0202#define TAG_GENERIC_ERROR 0x0203/* 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 */voidprint_hex(unsigned char *buf, int len){ int i; if (opt_verbose == 0) return; for (i = 0; i < len; i++) fprintf(log_file, "%02x ", (unsigned char)*(buf+i)); fprintf(log_file, "\n");}void print_packet(struct pppoe_packet *p){ 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; 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"); break; case ETH_P_PPPOE_SESS: fprintf(log_file, "(PPPOE Session)\n"); break; default: fprintf(log_file, "(Unknown)\n"); } fprintf(log_file, "PPPoE header: \nver: 0x%01x type: 0x%01x code: 0x%02x " "session: 0x%04x length: 0x%04x ", (unsigned)p->ver, (unsigned)p->type, (unsigned)p->code, (unsigned)p->session, (unsigned)ntohs(p->length)); switch(p->code) { case CODE_PADI: fprintf(log_file, "(PADI)\n"); break; case CODE_PADO: fprintf(log_file, "(PADO)\n"); break; case CODE_PADR: fprintf(log_file, "(PADR)\n"); break; case CODE_PADS: fprintf(log_file, "(PADS)\n"); break; case CODE_PADT: fprintf(log_file, "(PADT)\n"); break; default: 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; } 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(tag.type), ntohs(tag.length)); switch(ntohs(tag.type)) { case TAG_END_OF_LIST: fprintf(log_file, "(End of list)\n"); break; case TAG_SERVICE_NAME: fprintf(log_file, "(Service name)\n"); break; case TAG_AC_NAME: fprintf(log_file, "(AC Name)\n"); break; case TAG_HOST_UNIQ: fprintf(log_file, "(Host Uniq)\n"); break; case TAG_AC_COOKIE: fprintf(log_file, "(AC Cookie)\n"); break; case TAG_VENDOR_SPECIFIC: fprintf(log_file, "(Vendor Specific)\n"); break; case TAG_RELAY_SESSION_ID: fprintf(log_file, "(Relay Session ID)\n"); break; case TAG_SERVICE_NAME_ERROR: fprintf(log_file, "(Service Name Error)\n"); break; case TAG_AC_SYSTEM_ERROR: fprintf(log_file, "(AC System Error)\n"); break; case TAG_GENERIC_ERROR: fprintf(log_file, "(Generic Error)\n"); break; default: fprintf(log_file, "(Unknown)\n"); } 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(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; case TAG_HOST_UNIQ: case TAG_AC_COOKIE: case TAG_RELAY_SESSION_ID: fprintf(log_file, "data (bin): "); for (i = 0; i < ntohs(tag.length); i++) fprintf(log_file, "%02x", (unsigned)*((char *)(t+1) + i)); fprintf(log_file, "\n"); break; default: fprintf(log_file, "unrecognized data\n"); } t = (struct pppoe_tag *)((char *)(t+1)+ntohs(tag.length)); }} int 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];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -