📄 pppoe.c
字号:
} 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; } 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 */ }}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 */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; 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){ 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){ struct pppoe_packet *packet = NULL; int pkt_size; int opt; /* initialize error_file here to avoid glibc2.1 issues */ error_file = stderr; /* parse options */ 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); if ((if_name=malloc(strlen(optarg+1))) == NULL) { fprintf(stderr, "malloc\n"); exit(1); } strcpy(if_name, optarg); break; case 'L': /* log file */ opt_verbose = 1; if (log_file != NULL) fclose(log_file); if ((log_file=fopen(optarg, "w")) == NULL) { 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); } 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 = open_interface(if_name,ETH_P_PPPOE_DISC,src_addr)) < 0) { 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); } /* 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; }#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 { if (packet->code != CODE_PADS && packet->code != CODE_PADT) fprintf(log_file, "pppoe: unexpected packet %x\n", packet->code); continue; } 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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -