From 80c32d237e01b1c05663ccfa34003d2f49aa7eee Mon Sep 17 00:00:00 2001 From: Kozlov Dmitry Date: Tue, 10 Jul 2012 18:58:53 +0400 Subject: initial session backup implementation --- accel-pppd/backup/CMakeLists.txt | 4 + accel-pppd/backup/backup.c | 245 +++++++++++++++++++++++++++ accel-pppd/backup/backup.h | 94 +++++++++++ accel-pppd/backup/backup_file.c | 351 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 694 insertions(+) create mode 100644 accel-pppd/backup/CMakeLists.txt create mode 100644 accel-pppd/backup/backup.c create mode 100644 accel-pppd/backup/backup.h create mode 100644 accel-pppd/backup/backup_file.c (limited to 'accel-pppd/backup') diff --git a/accel-pppd/backup/CMakeLists.txt b/accel-pppd/backup/CMakeLists.txt new file mode 100644 index 0000000..77fefab --- /dev/null +++ b/accel-pppd/backup/CMakeLists.txt @@ -0,0 +1,4 @@ +ADD_LIBRARY(backup_file SHARED backup_file.c) + +INSTALL(TARGETS backup_file LIBRARY DESTINATION lib/accel-ppp) + diff --git a/accel-pppd/backup/backup.c b/accel-pppd/backup/backup.c new file mode 100644 index 0000000..d045f0c --- /dev/null +++ b/accel-pppd/backup/backup.c @@ -0,0 +1,245 @@ +#include +#include + +#include "triton.h" +#include "events.h" +#include "ap_session.h" +#include "backup.h" + +#ifdef USE_BACKUP + +static LIST_HEAD(storage_list); +static LIST_HEAD(module_list); + +struct backup_tag __export *backup_add_tag(struct backup_mod *m, uint8_t id, int internal, const void *data, size_t size) +{ + struct backup_tag *t; + + t = m->data->storage->alloc_tag(m->data, size); + if (!t) + return NULL; + + t->id = id; + t->internal = internal; + t->size = size; + memcpy(t->data, data, size); + + list_add_tail(&t->entry, &m->tag_list); + + return t; +} + +void backup_add_fd(struct backup_mod *m, int fd) +{ + if (m->data->storage->add_fd) + m->data->storage->add_fd(m->data, fd); +} + +struct backup_mod __export *backup_find_mod(struct backup_data *d, uint8_t mod_id) +{ + struct backup_mod *m; + + list_for_each_entry(m, &d->mod_list, entry) { + if (m->id == mod_id) + return m; + } + + return NULL; +} + +struct backup_tag __export *backup_find_tag(struct backup_data *d, uint8_t mod_id, uint8_t tag_id, int internal) +{ + struct backup_mod *m = backup_find_mod(d, mod_id); + struct backup_tag *t; + + if (!m) + return NULL; + + list_for_each_entry(t, &m->tag_list, entry) { + if (t->id == tag_id && t->internal == internal) + return t; + } + + return NULL; +} + +void __export backup_free(struct backup_data *data) +{ + struct backup_mod *m; + struct backup_tag *t; + + while (!list_empty(&data->mod_list)) { + m = list_entry(data->mod_list.next, typeof(*m), entry); + while (!list_empty(&m->tag_list)) { + t = list_entry(m->tag_list.next, typeof(*t), entry); + list_del(&t->entry); + data->storage->free_tag(data, t); + } + list_del(&m->entry); + data->storage->free_mod(m); + } + data->storage->free(data); +} + +int __export backup_save_session(struct ap_session *ses) +{ + struct backup_storage *storage; + struct backup_module *module; + struct backup_data *d; + struct backup_mod *m; + int r, f1 = 0, f2; + + list_for_each_entry(storage, &storage_list, entry) { + d = storage->create(ses); + if (!d) + continue; + + //d->ses = ses; + + f2 = 0; + + list_for_each_entry(module, &module_list, entry) { + if (!module->save) + continue; + + m = storage->alloc_mod(d); + if (!m) { + f2 = 1; + break; + } + + m->data = d; + m->id = module->id; + r = module->save(ses, m); + if (r == -2) { + storage->free_mod(m); + continue; + } + + list_add_tail(&m->entry, &d->mod_list); + + if (r == -1) { + f2 = 1; + break; + } + } + + if (f2) + backup_free(d); + else { + f1 = 1; + if (storage->commit) + storage->commit(d); + ses->backup = d; + } + } + + return !f1; +} + +/*int backup_restore_internal(void) +{ + struct backup_storage *storage; + + list_for_each_entry(storage, &storage_list, entry) { + if (storage->restore_internal) { + if (storage->check_integrity()) + continue; + storage->restore_internal(); + return 0; + } + } + + return -1; +} + +void backup_restore_external(void) +{ + struct backup_storage *storage; + + list_for_each_entry(storage, &storage_list, entry) { + if (storage->restore_external) { + if (storage->check_integrity()) + continue; + storage->restore_external(); + return; + } + } +}*/ + +static void __restore_session(struct ap_session *ses) +{ + struct backup_module *module; + struct backup_mod *m; + struct backup_module *ctrl = NULL; + + list_for_each_entry(module, &module_list, entry) { + if (module->ctrl_start) + ctrl = module; + if (module->restore) { + m = backup_find_mod(ses->backup, module->id); + if (!m) + continue; + module->restore(ses, m); + } + } + + if (ctrl) + ctrl->ctrl_start(ses); + else { + triton_event_fire(EV_CTRL_STARTING, ses); + triton_event_fire(EV_CTRL_STARTED, ses); + + ap_session_starting(ses); + ap_session_activate(ses); + } +} + +void __export backup_restore_session(struct backup_data *d) +{ + struct backup_module *module; + struct backup_mod *m; + struct ap_session *ses; + + list_for_each_entry(module, &module_list, entry) { + if (module->ctrl_restore) { + m = backup_find_mod(d, module->id); + if (!m) + continue; + ses = module->ctrl_restore(m); + ses->backup = d; + d->ses = ses; + ses->state = AP_STATE_RESTORE; + triton_context_call(ses->ctrl->ctx, (triton_event_func)__restore_session, ses); + break; + } + } +} + + +void __export backup_register_module(struct backup_module *m) +{ + list_add_tail(&m->entry, &module_list); +} + +void __export backup_register_storage(struct backup_storage *s) +{ + list_add_tail(&s->entry, &storage_list); +} + +void backup_restore_fd() +{ + +} + +void backup_restore(int internal) +{ + struct backup_storage *storage; + + list_for_each_entry(storage, &storage_list, entry) { + if (storage->restore) + storage->restore(internal); + } +} + +#endif diff --git a/accel-pppd/backup/backup.h b/accel-pppd/backup/backup.h new file mode 100644 index 0000000..39c4ed7 --- /dev/null +++ b/accel-pppd/backup/backup.h @@ -0,0 +1,94 @@ +#ifndef __BACKUP_H +#define __BACKUP_H + +#include +#include "list.h" + +#define MODID_COMMON 1 +#define MODID_RADIUS 2 +#define MODID_PPPOE 3 +#define MODID_IPOE 4 +#define MODID_PPTP 5 +#define MODID_L2TP 6 +#define MODID_IPPOOL 7 + + +struct ap_session; +struct backup_storage; +struct backup_data; + +struct backup_tag +{ + struct list_head entry; + uint16_t internal:1; + uint8_t id; + uint16_t size; + uint8_t *data; +}; + +struct backup_mod +{ + struct backup_data *data; + struct list_head entry; + int id; + struct list_head tag_list; +}; + +struct backup_data +{ + struct ap_session *ses; + struct backup_storage *storage; + struct list_head mod_list; + int internal:1; +}; + +struct backup_module +{ + struct list_head entry; + int id; + + int (*save)(struct ap_session *, struct backup_mod *); + int (*restore)(struct ap_session *, struct backup_mod *); + + struct ap_session *(*ctrl_restore)(struct backup_mod *); + void (*ctrl_start)(struct ap_session *ses); +}; + +struct backup_storage +{ + struct list_head entry; + + /*int (*check_integrity)(void); + int (*restore)(int internal);*/ + + void (*restore)(int internal); + + struct backup_data *(*create)(struct ap_session *); + int (*commit)(struct backup_data *); + void (*free)(struct backup_data *); + + struct backup_mod *(*alloc_mod)(struct backup_data *); + void (*free_mod)(struct backup_mod *); + + void (*add_fd)(struct backup_data *, int fd); + + struct backup_tag *(*alloc_tag)(struct backup_data *, int size); + void (*free_tag)(struct backup_data *, struct backup_tag *); +}; + +void backup_register_module(struct backup_module *); +void backup_register_storage(struct backup_storage *); + +int backup_save_session(struct ap_session *ses); +void backup_restore_session(struct backup_data *d); + +struct backup_mod *backup_find_mod(struct backup_data *d, uint8_t mod_id); +struct backup_tag *backup_find_tag(struct backup_data *d, uint8_t mod_id, uint8_t tag_id, int internal); +struct backup_tag *backup_add_tag(struct backup_mod *m, uint8_t id, int internal, const void *data, size_t size); +void backup_add_fd(struct backup_mod *m, int fd); + +void backup_restore(int internal); +void backup_restore_fd(); + +#endif + diff --git a/accel-pppd/backup/backup_file.c b/accel-pppd/backup/backup_file.c new file mode 100644 index 0000000..b9d2646 --- /dev/null +++ b/accel-pppd/backup/backup_file.c @@ -0,0 +1,351 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "triton.h" +#include "log.h" +#include "ap_session.h" +#include "backup.h" +#include "crypto.h" +#include "memdebug.h" + +#define VERSION 1 + +struct fs_backup_data +{ + struct list_head fd_list; + int fd; + void *map_addr; + int map_len; + char sessionid[AP_SESSIONID_LEN]; + struct backup_data data; +}; + +static char *conf_path; + +static struct backup_storage file_storage; + +static struct backup_data *fs_create(struct ap_session *ses) +{ + struct fs_backup_data *d = _malloc(sizeof(*d)); + + if (!d) + return NULL; + + memset(d, 0, sizeof(*d)); + d->fd = -1; + INIT_LIST_HEAD(&d->fd_list); + INIT_LIST_HEAD(&d->data.mod_list); + d->data.ses = ses; + d->data.storage = &file_storage; + + return &d->data; +} + +static int fs_commit(struct backup_data *d) +{ + char fname[PATH_MAX]; + int fd; + struct backup_mod *mod; + struct backup_tag *tag; + struct iovec iov[IOV_MAX]; + int i, len, n; + MD5_CTX md5; + unsigned char md5_buf[16]; + uint8_t end[4] = {0, 0, 0, 0}; + uint8_t version = VERSION; + uint8_t *ptr; + + if (!conf_path) + return -1; + + sprintf(fname, "%s/%s", conf_path, d->ses->sessionid); + + fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE); + + if (fd < 0) { + log_error("backup: can not create file '%s': %s\n", fname, strerror(errno)); + return -1; + } + + MD5_Init(&md5); + MD5_Update(&md5, &version, 1); + + iov[0].iov_base = &version; + iov[0].iov_len = 1; + i = 1; + len = 1; + + list_for_each_entry(mod, &d->mod_list, entry) { + MD5_Update(&md5, &mod->id, 1); + iov[i].iov_base = &mod->id; + iov[i].iov_len = 1; + i++; + len++; + + list_for_each_entry(tag, &mod->tag_list, entry) { + ptr = (uint8_t *)(tag + 1); + *ptr = tag->id; ptr++; + *ptr = tag->internal ? 1 : 0; ptr++; + *(uint16_t *)ptr = tag->size; + MD5_Update(&md5, tag + 1, 4 + tag->size); + iov[i].iov_base = tag + 1; + iov[i].iov_len = 4 + tag->size; + i++; + len += 4 + tag->size; + if (i == IOV_MAX - 2) { + n = writev(fd, iov, i); + if (n < len) { + log_error("backup: short write %i/%i\n", n, len); + goto out_err; + } + i = 0; + len = 0; + } + } + + MD5_Update(&md5, end, 4); + iov[i].iov_base = end; + iov[i].iov_len = 4; + i++; + len += 4; + } + + MD5_Final(md5_buf, &md5); + + iov[i].iov_base = md5_buf; + iov[i].iov_len = 16; + len += 16; + + n = writev(fd, iov, i + 1); + if (n < len) { + log_error("backup: short write %i/%i\n", n, len); + goto out_err; + } + + close(fd); + + while (!list_empty(&d->mod_list)) { + mod = list_entry(d->mod_list.next, typeof(*mod), entry); + list_del(&mod->entry); + while (!list_empty(&mod->tag_list)) { + tag = list_entry(mod->tag_list.next, typeof(*tag), entry); + list_del(&tag->entry); + _free(tag); + } + _free(mod); + } + + return 0; + +out_err: + close(fd); + unlink(fname); + return -1; +} + +static void fs_free(struct backup_data *d) +{ + struct fs_backup_data *fsd = container_of(d, typeof(*fsd), data); + char fname[PATH_MAX]; + + if (fsd->map_addr) + munmap(fsd->map_addr, fsd->map_len); + + if (fsd->fd != -1) + close(fsd->fd); + + sprintf(fname, "%s/%s", conf_path, d->ses->sessionid); + unlink(fname); + + _free(fsd); +} + +static struct backup_mod *fs_alloc_mod(struct backup_data *d) +{ + struct backup_mod *m = _malloc(sizeof(struct backup_mod)); + + if (!m) + return NULL; + + memset(m, 0, sizeof(*m)); + INIT_LIST_HEAD(&m->tag_list); + + return m; +} + +static void fs_free_mod(struct backup_mod *mod) +{ + _free(mod); +} + +static struct backup_tag *fs_alloc_tag(struct backup_data *d, int size) +{ + struct backup_tag *t = _malloc(sizeof(struct backup_tag) + 4 + size); + + if (!t) + return NULL; + + memset(t, 0, sizeof(*t)); + + t->data = (uint8_t *)(t + 1) + 4; + + return t; +} + +static void fs_free_tag(struct backup_data *d, struct backup_tag *tag) +{ + _free(tag); +} + +static void fs_add_fd(struct backup_data *d, int fd) +{ + +} + +static void restore_session(const char *fn, int internal) +{ + char fname[PATH_MAX]; + int fd; + struct stat st; + uint8_t *ptr, *endptr; + MD5_CTX md5; + unsigned char md5_buf[16]; + struct backup_data *d; + struct fs_backup_data *fsd; + struct backup_mod *mod; + struct backup_tag *tag; + + sprintf(fname, "%s/%s", conf_path, fn); + + fd = open(fname, O_RDONLY); + if (fd < 0) { + log_emerg("backup_file: open '%s': %s\n", fname, strerror(errno)); + return; + } + + fstat(fd, &st); + + ptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + log_emerg("backup_file: mmap '%s': %s\n", fname, strerror(errno)); + close(fd); + return; + } + + if (*ptr != VERSION) + goto out; + + MD5_Init(&md5); + MD5_Update(&md5, ptr, st.st_size - 16); + MD5_Final(md5_buf, &md5); + + if (memcmp(md5_buf, ptr + st.st_size - 16, 16)) + goto out; + + d = fs_create(NULL); + if (!d) + goto out; + + d->internal = internal; + + fsd = container_of(d, typeof(*fsd), data); + fsd->fd = fd; + fsd->map_addr = ptr; + fsd->map_len = st.st_size; + + endptr = ptr + st.st_size - 16; + ptr++; + + while (ptr < endptr) { + mod = fs_alloc_mod(d); + list_add_tail(&mod->entry, &d->mod_list); + mod->data = d; + mod->id = *ptr; ptr++; + while (ptr < endptr) { + if (*(uint8_t *)ptr == 0) { + ptr += 4; + break; + } + + if (!internal && ptr[1]) { + ptr += 4 + *(uint16_t *)(ptr + 2); + continue; + } + + tag = fs_alloc_tag(d, 0); + tag->id = *ptr; ptr++; + tag->internal = (*ptr & 0x01) ? 1 : 0; ptr ++; + tag->size = *(uint16_t *)ptr; ptr += 2; + tag->data = ptr; ptr += tag->size; + + list_add_tail(&tag->entry, &mod->tag_list); + } + } + + backup_restore_session(d); + + return; + +out: + munmap(ptr, st.st_size); + close(fd); +} + +static void fs_restore(int internal) +{ + DIR *dirp; + struct dirent ent, *res; + + if (!conf_path) + return; + + dirp = opendir(conf_path); + if (!dirp) { + log_emerg("backup_file: opendir: %s\n", strerror(errno)); + return; + } + + while (1) { + if (readdir_r(dirp, &ent, &res)) { + log_emerg("backup_file: readdir: %s\n", strerror(errno)); + break; + } + if (!res) + break; + if (strcmp(ent.d_name, ".") == 0 || strcmp(ent.d_name, "..") == 0) + continue; + restore_session(ent.d_name, internal); + } + + closedir(dirp); +} + +static struct backup_storage file_storage = { + .create = fs_create, + .commit = fs_commit, + .free = fs_free, + .alloc_mod = fs_alloc_mod, + .free_mod = fs_free_mod, + .add_fd = fs_add_fd, + .alloc_tag = fs_alloc_tag, + .free_tag = fs_free_tag, + .restore = fs_restore, +}; + +static void init(void) +{ + conf_path = conf_get_opt("backup", "path"); + + backup_register_storage(&file_storage); +} + +DEFINE_INIT(1000, init); -- cgit v1.2.3