diff options
Diffstat (limited to 'accel-pppd/ppp/ppp.c')
-rw-r--r-- | accel-pppd/ppp/ppp.c | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/accel-pppd/ppp/ppp.c b/accel-pppd/ppp/ppp.c new file mode 100644 index 0000000..f578e8e --- /dev/null +++ b/accel-pppd/ppp/ppp.c @@ -0,0 +1,681 @@ +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <features.h> +#include <signal.h> +#include "linux_ppp.h" + +#include <openssl/md5.h> + +#include "triton.h" + +#include "events.h" +#include "ppp.h" +#include "ppp_fsm.h" +#include "log.h" +#include "spinlock.h" + +#include "memdebug.h" + +int __export conf_ppp_verbose; +static int conf_sid_ucase; + +pthread_rwlock_t __export ppp_lock = PTHREAD_RWLOCK_INITIALIZER; +__export LIST_HEAD(ppp_list); + +static LIST_HEAD(layers); +int __export sock_fd; + +int __export ppp_shutdown; + +static unsigned long long seq; +#if __WORDSIZE == 32 +static spinlock_t seq_lock; +#endif + + +struct ppp_stat_t ppp_stat; + +struct layer_node_t +{ + struct list_head entry; + int order; + struct list_head items; +}; + +static int ppp_chan_read(struct triton_md_handler_t*); +static int ppp_unit_read(struct triton_md_handler_t*); +static void init_layers(struct ppp_t *); +static void _free_layers(struct ppp_t *); +static void start_first_layer(struct ppp_t *); + +void __export ppp_init(struct ppp_t *ppp) +{ + memset(ppp,0,sizeof(*ppp)); + INIT_LIST_HEAD(&ppp->layers); + INIT_LIST_HEAD(&ppp->chan_handlers); + INIT_LIST_HEAD(&ppp->unit_handlers); + INIT_LIST_HEAD(&ppp->pd_list); +} + +static void _free_ppp(struct ppp_t *ppp) +{ + if (ppp->chan_buf) + free(ppp->chan_buf); + if (ppp->unit_buf) + _free(ppp->unit_buf); + if (ppp->username) + _free(ppp->username); +} + +static void generate_sessionid(struct ppp_t *ppp) +{ + unsigned long long sid; + +#if __WORDSIZE == 32 + spin_lock(&seq_lock); + sid = ++seq; + spin_unlock(&seq_lock); +#else + sid = __sync_add_and_fetch(&seq, 1); +#endif + + if (conf_sid_ucase) + sprintf(ppp->sessionid, "%016llX", sid); + else + sprintf(ppp->sessionid, "%016llx", sid); +} + +int __export establish_ppp(struct ppp_t *ppp) +{ + /* Open an instance of /dev/ppp and connect the channel to it */ + if (ioctl(ppp->fd, PPPIOCGCHAN, &ppp->chan_idx) == -1) { + log_ppp_error("ioctl(PPPIOCGCHAN): %s\n", strerror(errno)); + return -1; + } + + ppp->chan_fd = open("/dev/ppp", O_RDWR); + if (ppp->chan_fd < 0) { + log_ppp_error("open(chan) /dev/ppp: %s\n", strerror(errno)); + return -1; + } + + if (ioctl(ppp->chan_fd, PPPIOCATTCHAN, &ppp->chan_idx) < 0) { + log_ppp_error("ioctl(PPPIOCATTCHAN): %s\n", strerror(errno)); + goto exit_close_chan; + } + + ppp->unit_fd = open("/dev/ppp", O_RDWR); + if (ppp->unit_fd < 0) { + log_ppp_error("open(unit) /dev/ppp: %s\n", strerror(errno)); + goto exit_close_chan; + } + + ppp->unit_idx = -1; + if (ioctl(ppp->unit_fd, PPPIOCNEWUNIT, &ppp->unit_idx) < 0) { + log_ppp_error("ioctl(PPPIOCNEWUNIT): %s\n", strerror(errno)); + goto exit_close_unit; + } + + if (ioctl(ppp->chan_fd, PPPIOCCONNECT, &ppp->unit_idx) < 0) { + log_ppp_error("ioctl(PPPIOCCONNECT): %s\n", strerror(errno)); + goto exit_close_unit; + } + + if (fcntl(ppp->chan_fd, F_SETFL, O_NONBLOCK)) { + log_ppp_error("ppp: cann't to set nonblocking mode: %s\n", strerror(errno)); + goto exit_close_unit; + } + + if (fcntl(ppp->unit_fd, F_SETFL, O_NONBLOCK)) { + log_ppp_error("ppp: cann't to set nonblocking mode: %s\n", strerror(errno)); + goto exit_close_unit; + } + + ppp->start_time = time(NULL); + generate_sessionid(ppp); + sprintf(ppp->ifname, "ppp%i", ppp->unit_idx); + + log_ppp_info1("connect: %s <--> %s(%s)\n", ppp->ifname, ppp->ctrl->name, ppp->chan_name); + + init_layers(ppp); + + if (list_empty(&ppp->layers)) { + log_ppp_error("no layers to start\n"); + goto exit_close_unit; + } + + ppp->chan_buf = _malloc(PPP_MRU); + ppp->unit_buf = _malloc(PPP_MRU); + + ppp->chan_hnd.fd = ppp->chan_fd; + ppp->chan_hnd.read = ppp_chan_read; + ppp->unit_hnd.fd = ppp->unit_fd; + ppp->unit_hnd.read = ppp_unit_read; + triton_md_register_handler(ppp->ctrl->ctx, &ppp->chan_hnd); + triton_md_register_handler(ppp->ctrl->ctx, &ppp->unit_hnd); + + triton_md_enable_handler(&ppp->chan_hnd, MD_MODE_READ); + triton_md_enable_handler(&ppp->unit_hnd, MD_MODE_READ); + + ppp->state = PPP_STATE_STARTING; + __sync_add_and_fetch(&ppp_stat.starting, 1); + + pthread_rwlock_wrlock(&ppp_lock); + list_add_tail(&ppp->entry, &ppp_list); + pthread_rwlock_unlock(&ppp_lock); + + log_ppp_debug("ppp established\n"); + + triton_event_fire(EV_PPP_STARTING, ppp); + + start_first_layer(ppp); + + return 0; + +exit_close_unit: + close(ppp->unit_fd); +exit_close_chan: + close(ppp->chan_fd); + + _free_ppp(ppp); + + return -1; +} + +static void destablish_ppp(struct ppp_t *ppp) +{ + pthread_rwlock_wrlock(&ppp_lock); + list_del(&ppp->entry); + pthread_rwlock_unlock(&ppp_lock); + + switch (ppp->state) { + case PPP_STATE_ACTIVE: + __sync_sub_and_fetch(&ppp_stat.active, 1); + break; + case PPP_STATE_STARTING: + __sync_sub_and_fetch(&ppp_stat.starting, 1); + break; + case PPP_STATE_FINISHING: + __sync_sub_and_fetch(&ppp_stat.finishing, 1); + break; + } + + triton_md_unregister_handler(&ppp->chan_hnd); + triton_md_unregister_handler(&ppp->unit_hnd); + + close(ppp->unit_fd); + close(ppp->chan_fd); + close(ppp->fd); + + ppp->unit_fd = -1; + ppp->chan_fd = -1; + ppp->fd = -1; + + _free(ppp->unit_buf); + _free(ppp->chan_buf); + + _free_layers(ppp); + + ppp->terminated = 1; + + log_ppp_debug("ppp destablished\n"); + + triton_event_fire(EV_PPP_FINISHED, ppp); + ppp->ctrl->finished(ppp); + + if (ppp->username) { + _free(ppp->username); + ppp->username = NULL; + } + + if (ppp_shutdown && !ppp_stat.starting && !ppp_stat.active && !ppp_stat.finishing) + kill(getpid(), SIGTERM); +} + +/*void print_buf(uint8_t *buf, int size) +{ + int i; + for(i=0;i<size;i++) + printf("%x ",buf[i]); + printf("\n"); +}*/ + +int __export ppp_chan_send(struct ppp_t *ppp, void *data, int size) +{ + int n; + + //printf("ppp_chan_send: "); + //print_buf((uint8_t*)data,size); + + n = write(ppp->chan_fd,data,size); + if (n < size) + log_ppp_error("ppp_chan_send: short write %i, excpected %i\n", n, size); + return n; +} + +int __export ppp_unit_send(struct ppp_t *ppp, void *data, int size) +{ + int n; + + //printf("ppp_unit_send: "); + //print_buf((uint8_t*)data,size); + + n=write(ppp->unit_fd, data, size); + if (n < size) + log_ppp_error("ppp_unit_send: short write %i, excpected %i\n",n,size); + return n; +} + +static int ppp_chan_read(struct triton_md_handler_t *h) +{ + struct ppp_t *ppp = container_of(h, typeof(*ppp), chan_hnd); + struct ppp_handler_t *ppp_h; + uint16_t proto; + + while(1) { +cont: + ppp->chan_buf_size = read(h->fd, ppp->chan_buf, PPP_MRU); + if (ppp->chan_buf_size < 0) { + if (errno == EAGAIN) + return 0; + log_ppp_error("ppp_chan_read: %s\n", strerror(errno)); + return 0; + } + + //printf("ppp_chan_read: "); + //print_buf(ppp->chan_buf,ppp->chan_buf_size); + if (ppp->chan_buf_size == 0) { + ppp_terminate(ppp, 1, TERM_NAS_ERROR); + return 1; + } + + if (ppp->chan_buf_size < 2) { + log_ppp_error("ppp_chan_read: short read %i\n", ppp->chan_buf_size); + continue; + } + + proto = ntohs(*(uint16_t*)ppp->chan_buf); + list_for_each_entry(ppp_h, &ppp->chan_handlers, entry) { + if (ppp_h->proto == proto) { + ppp_h->recv(ppp_h); + if (ppp->chan_fd == -1) { + ppp->ctrl->finished(ppp); + return 1; + } + goto cont; + } + } + + lcp_send_proto_rej(ppp, proto); + //log_ppp_warn("ppp_chan_read: discarding unknown packet %x\n", proto); + } +} + +static int ppp_unit_read(struct triton_md_handler_t *h) +{ + struct ppp_t *ppp = container_of(h, typeof(*ppp), unit_hnd); + struct ppp_handler_t *ppp_h; + uint16_t proto; + + while (1) { +cont: + ppp->unit_buf_size = read(h->fd, ppp->unit_buf, PPP_MRU); + if (ppp->unit_buf_size < 0) { + if (errno == EAGAIN) + return 0; + log_ppp_error("ppp_unit_read: %s\n",strerror(errno)); + return 0; + } + + md_check(ppp->unit_buf); + //printf("ppp_unit_read: "); + //print_buf(ppp->unit_buf,ppp->unit_buf_size); + + if (ppp->unit_buf_size == 0) { + ppp_terminate(ppp, 1, TERM_NAS_ERROR); + return 1; + } + + if (ppp->unit_buf_size < 2) { + log_ppp_error("ppp_unit_read: short read %i\n", ppp->unit_buf_size); + continue; + } + + proto=ntohs(*(uint16_t*)ppp->unit_buf); + list_for_each_entry(ppp_h, &ppp->unit_handlers, entry) { + if (ppp_h->proto == proto) { + ppp_h->recv(ppp_h); + if (ppp->unit_fd == -1) { + ppp->ctrl->finished(ppp); + return 1; + } + goto cont; + } + } + lcp_send_proto_rej(ppp, proto); + //log_ppp_warn("ppp_unit_read: discarding unknown packet %x\n", proto); + } +} + +void ppp_recv_proto_rej(struct ppp_t *ppp, uint16_t proto) +{ + struct ppp_handler_t *ppp_h; + + list_for_each_entry(ppp_h, &ppp->chan_handlers, entry) { + if (ppp_h->proto == proto) { + if (ppp_h->recv_proto_rej) + ppp_h->recv_proto_rej(ppp_h); + return; + } + } + + list_for_each_entry(ppp_h, &ppp->unit_handlers, entry) { + if (ppp_h->proto == proto) { + if (ppp_h->recv_proto_rej) + ppp_h->recv_proto_rej(ppp_h); + return; + } + } +} + +void __export ppp_layer_started(struct ppp_t *ppp, struct ppp_layer_data_t *d) +{ + struct layer_node_t *n = d->node; + + if (d->started) + return; + + d->started = 1; + + list_for_each_entry(d, &n->items, entry) + if (!d->started) return; + + if (n->entry.next == &ppp->layers) { + ppp->state = PPP_STATE_ACTIVE; + __sync_sub_and_fetch(&ppp_stat.starting, 1); + __sync_add_and_fetch(&ppp_stat.active, 1); + ppp->ctrl->started(ppp); + triton_event_fire(EV_PPP_STARTED, ppp); + } else { + n = list_entry(n->entry.next, typeof(*n), entry); + list_for_each_entry(d, &n->items, entry) { + d->starting = 1; + if (d->layer->start(d)) { + ppp_terminate(ppp, TERM_NAS_ERROR, 0); + return; + } + } + } +} + +void __export ppp_layer_finished(struct ppp_t *ppp, struct ppp_layer_data_t *d) +{ + struct layer_node_t *n = d->node; + + d->finished = 1; + d->starting = 0; + + list_for_each_entry(n, &ppp->layers, entry) { + list_for_each_entry(d, &n->items, entry) { + if (d->starting && !d->finished) + return; + } + } + + destablish_ppp(ppp); +} + +void __export ppp_terminate(struct ppp_t *ppp, int cause, int hard) +{ + struct layer_node_t *n; + struct ppp_layer_data_t *d; + int s = 0; + + if (ppp->terminated) + return; + + if (!ppp->stop_time) + time(&ppp->stop_time); + + if (!ppp->terminate_cause) + ppp->terminate_cause = cause; + + if (ppp->terminating) { + if (hard) + destablish_ppp(ppp); + return; + } + + ppp->terminating = 1; + if (ppp->state == PPP_STATE_ACTIVE) + __sync_sub_and_fetch(&ppp_stat.active, 1); + else + __sync_sub_and_fetch(&ppp_stat.starting, 1); + __sync_add_and_fetch(&ppp_stat.finishing, 1); + ppp->state = PPP_STATE_FINISHING; + + log_ppp_debug("ppp_terminate\n"); + + triton_event_fire(EV_PPP_FINISHING, ppp); + + if (hard) { + destablish_ppp(ppp); + return; + } + + list_for_each_entry(n,&ppp->layers,entry) { + list_for_each_entry(d,&n->items,entry) { + if (d->starting) { + s = 1; + d->layer->finish(d); + } + } + } + if (s) + return; + destablish_ppp(ppp); +} + +void __export ppp_register_chan_handler(struct ppp_t *ppp,struct ppp_handler_t *h) +{ + list_add_tail(&h->entry,&ppp->chan_handlers); +} +void __export ppp_register_unit_handler(struct ppp_t *ppp,struct ppp_handler_t *h) +{ + list_add_tail(&h->entry,&ppp->unit_handlers); +} +void __export ppp_unregister_handler(struct ppp_t *ppp,struct ppp_handler_t *h) +{ + list_del(&h->entry); +} + +static int get_layer_order(const char *name) +{ + if (!strcmp(name,"lcp")) return 0; + if (!strcmp(name,"auth")) return 1; + if (!strcmp(name,"ccp")) return 2; + if (!strcmp(name,"ipcp")) return 2; + return -1; +} + +int __export ppp_register_layer(const char *name, struct ppp_layer_t *layer) +{ + int order; + struct layer_node_t *n,*n1; + + order = get_layer_order(name); + + if (order < 0) + return order; + + list_for_each_entry(n, &layers, entry) { + if (order > n->order) + continue; + if (order < n->order) { + n1 = _malloc(sizeof(*n1)); + memset(n1, 0, sizeof(*n1)); + n1->order = order; + INIT_LIST_HEAD(&n1->items); + list_add_tail(&n1->entry, &n->entry); + n = n1; + } + goto insert; + } + n1 = _malloc(sizeof(*n1)); + memset(n1, 0, sizeof(*n1)); + n1->order = order; + INIT_LIST_HEAD(&n1->items); + list_add_tail(&n1->entry, &layers); + n = n1; +insert: + list_add_tail(&layer->entry, &n->items); + + return 0; +} +void __export ppp_unregister_layer(struct ppp_layer_t *layer) +{ + list_del(&layer->entry); +} + +static void init_layers(struct ppp_t *ppp) +{ + struct layer_node_t *n, *n1; + struct ppp_layer_t *l; + struct ppp_layer_data_t *d; + + list_for_each_entry(n,&layers,entry) { + n1 = _malloc(sizeof(*n1)); + memset(n1, 0, sizeof(*n1)); + INIT_LIST_HEAD(&n1->items); + list_add_tail(&n1->entry, &ppp->layers); + list_for_each_entry(l, &n->items, entry) { + d = l->init(ppp); + d->layer = l; + d->started = 0; + d->node = n1; + list_add_tail(&d->entry, &n1->items); + } + } +} + +static void _free_layers(struct ppp_t *ppp) +{ + struct layer_node_t *n; + struct ppp_layer_data_t *d; + + while (!list_empty(&ppp->layers)) { + n = list_entry(ppp->layers.next, typeof(*n), entry); + while (!list_empty(&n->items)) { + d = list_entry(n->items.next, typeof(*d), entry); + list_del(&d->entry); + d->layer->free(d); + } + list_del(&n->entry); + _free(n); + } +} + +static void start_first_layer(struct ppp_t *ppp) +{ + struct layer_node_t *n; + struct ppp_layer_data_t *d; + + n = list_entry(ppp->layers.next, typeof(*n), entry); + list_for_each_entry(d, &n->items, entry) { + d->starting = 1; + if (d->layer->start(d)) { + ppp_terminate(ppp, TERM_NAS_ERROR, 0); + return; + } + } +} + +struct ppp_layer_data_t *ppp_find_layer_data(struct ppp_t *ppp, struct ppp_layer_t *layer) +{ + struct layer_node_t *n; + struct ppp_layer_data_t *d; + + list_for_each_entry(n,&ppp->layers,entry) { + list_for_each_entry(d,&n->items,entry) { + if (d->layer == layer) + return d; + } + } + + return NULL; +} + +void ppp_shutdown_soft(void) +{ + ppp_shutdown = 1; + + if (!ppp_stat.starting && !ppp_stat.active && !ppp_stat.finishing) + kill(getpid(), SIGTERM); +} + +static void save_seq(void) +{ + FILE *f; + char *opt = conf_get_opt("ppp", "seq-file"); + if (!opt) + opt = "/var/run/accel-ppp/seq"; + + f = fopen(opt, "w"); + if (f) { + fprintf(f, "%llu", seq); + fclose(f); + } +} + +static void load_config(void) +{ + char *opt; + + opt = conf_get_opt("ppp", "verbose"); + if (opt && atoi(opt) > 0) + conf_ppp_verbose = 1; + + opt = conf_get_opt("ppp", "sid-case"); + if (opt) { + if (!strcmp(opt, "upper")) + conf_sid_ucase = 1; + else if (strcmp(opt, "lower")) + log_emerg("ppp: sid-case: invalid format\n"); + } +} + +static void __init init(void) +{ + char *opt; + FILE *f; + + sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (sock_fd < 0) { + perror("socket"); + _exit(EXIT_FAILURE); + } + + opt = conf_get_opt("ppp", "seq-file"); + if (!opt) + opt = "/var/run/accel-ppp/seq"; + + f = fopen(opt, "r"); + if (f) { + fscanf(f, "%llu", &seq); + fclose(f); + } else + seq = (unsigned long long)random() * (unsigned long long)random(); + + load_config(); + triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); + + atexit(save_seq); +} + |