diff options
Diffstat (limited to 'accel-pppd/extra')
-rw-r--r-- | accel-pppd/extra/CMakeLists.txt | 19 | ||||
-rw-r--r-- | accel-pppd/extra/chap-secrets.c | 281 | ||||
-rw-r--r-- | accel-pppd/extra/ippool.c | 208 | ||||
-rw-r--r-- | accel-pppd/extra/pppd_compat.c | 523 | ||||
-rw-r--r-- | accel-pppd/extra/shaper_tbf.c | 1274 | ||||
-rw-r--r-- | accel-pppd/extra/sigchld.c | 116 | ||||
-rw-r--r-- | accel-pppd/extra/sigchld.h | 21 |
7 files changed, 2442 insertions, 0 deletions
diff --git a/accel-pppd/extra/CMakeLists.txt b/accel-pppd/extra/CMakeLists.txt new file mode 100644 index 00000000..03d857ab --- /dev/null +++ b/accel-pppd/extra/CMakeLists.txt @@ -0,0 +1,19 @@ +ADD_LIBRARY(pppd_compat SHARED pppd_compat.c) +ADD_LIBRARY(ippool SHARED ippool.c) +ADD_LIBRARY(sigchld SHARED sigchld.c) +ADD_LIBRARY(chap-secrets SHARED chap-secrets.c) + +INSTALL(TARGETS pppd_compat ippool sigchld chap-secrets + LIBRARY DESTINATION lib/accel-ppp +) + +IF (SHAPER) + ADD_LIBRARY(shaper_tbf SHARED shaper_tbf.c) + IF (LIBNL_PREFIX) + SET_PROPERTY(TARGET shaper_tbf PROPERTY COMPILE_FLAGS "-I${LIBNL_PREFIX}/include") + SET_PROPERTY(TARGET shaper_tbf PROPERTY LINK_FLAGS "-L${LIBNL_PREFIX}/lib") + ENDIF (LIBNL_PREFIX) + TARGET_LINK_LIBRARIES(shaper_tbf nl m) + INSTALL(TARGETS shaper_tbf LIBRARY DESTINATION lib/accel-ppp) +ENDIF (SHAPER) + diff --git a/accel-pppd/extra/chap-secrets.c b/accel-pppd/extra/chap-secrets.c new file mode 100644 index 00000000..35503473 --- /dev/null +++ b/accel-pppd/extra/chap-secrets.c @@ -0,0 +1,281 @@ +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "pwdb.h" +#include "ipdb.h" +#include "ppp.h" +#include "events.h" +#include "triton.h" +#include "log.h" + +#include "memdebug.h" + +static char *def_chap_secrets = "/etc/ppp/chap-secrets"; +static char *conf_chap_secrets; +static in_addr_t conf_gw_ip_address = 0; + +static void *pd_key; +static struct ipdb_t ipdb; + +struct cs_pd_t +{ + struct ppp_pd_t pd; + struct ipdb_item_t ip; + char *passwd; + char *rate; +}; + +static char *skip_word(char *ptr) +{ + char quote = 0; + + if (*ptr == '\'' || *ptr == '"') { + quote = *ptr; + ptr++; + } + + for(; *ptr; ptr++) { + if (quote) { + if (*ptr == '\n') + break; + if (*ptr == '\\' && ptr[1] && ptr[1] != '\n') { + memmove(ptr, ptr + 1, strlen(ptr)); + continue; + } + if (*ptr == quote) { + *ptr = ' '; + break; + } + } else if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') + break; + } + return ptr; +} +static char *skip_space(char *ptr) +{ + for(; *ptr; ptr++) + if (*ptr != ' ' && *ptr != '\t') + break; + return ptr; +} +static int split(char *buf, char **ptr) +{ + int i; + + for (i = 0; i < 4; i++) { + buf = skip_word(buf); + if (!*buf) + return i; + + *buf = 0; + + buf = skip_space(buf + 1); + if (!*buf) + return i; + + if (*buf == '"' || *buf == '\'') + ptr[i] = buf + 1; + else + ptr[i] = buf; + } + + buf = skip_word(buf); + //if (*buf == '\n') + *buf = 0; + //else if (*buf) + // return -1; + + return i; +} + + +static struct cs_pd_t *create_pd(struct ppp_t *ppp, const char *username) +{ + FILE *f; + char *buf; + char *ptr[5]; + int n; + struct cs_pd_t *pd; + + if (!conf_chap_secrets) + return NULL; + + f = fopen(conf_chap_secrets, "r"); + if (!f) { + log_error("chap-secrets: open '%s': %s\n", conf_chap_secrets, strerror(errno)); + return NULL; + } + + buf = _malloc(4096); + if (!buf) { + log_emerg("chap-secrets: out of memory\n"); + fclose(f); + return NULL; + } + + while (fgets(buf, 4096, f)) { + if (buf[0] == '#') + continue; + n = split(buf, ptr); + if (n < 3) + continue; + if (*buf == '\'' || *buf == '"') { + if (!strcmp(buf + 1, username)) + goto found; + } else { + if (!strcmp(buf, username)) + goto found; + } + } + +out: + fclose(f); + _free(buf); + return NULL; + +found: + pd = _malloc(sizeof(*pd)); + if (!pd) { + log_emerg("chap-secrets: out of memory\n"); + goto out; + } + + memset(pd, 0, sizeof(*pd)); + pd->pd.key = &pd_key; + pd->passwd = _strdup(ptr[1]); + if (!pd->passwd) { + log_emerg("chap-secrets: out of memory\n"); + _free(pd); + goto out; + } + + pd->ip.addr = conf_gw_ip_address; + if (n >= 3) + pd->ip.peer_addr = inet_addr(ptr[2]); + pd->ip.owner = &ipdb; + + if (n == 4) + pd->rate = _strdup(ptr[3]); + + list_add_tail(&pd->pd.entry, &ppp->pd_list); + + fclose(f); + _free(buf); + + return pd; +} + +static struct cs_pd_t *find_pd(struct ppp_t *ppp) +{ + struct ppp_pd_t *pd; + + list_for_each_entry(pd, &ppp->pd_list, entry) { + if (pd->key == &pd_key) { + return container_of(pd, typeof(struct cs_pd_t), pd); + } + } + + return NULL; +} + +static void ev_ppp_finished(struct ppp_t *ppp) +{ + struct cs_pd_t *pd = find_pd(ppp); + + if (!pd) + return; + + list_del(&pd->pd.entry); + _free(pd->passwd); + if (pd->rate) + _free(pd->rate); + _free(pd); +} + +static void ev_ppp_pre_up(struct ppp_t *ppp) +{ + struct cs_pd_t *pd = find_pd(ppp); + struct ev_shaper_t ev = { + .ppp = ppp, + }; + + if (!pd) + return; + + if (pd->rate) { + ev.val = pd->rate; + triton_event_fire(EV_SHAPER, &ev); + } +} + +static struct ipdb_item_t *get_ip(struct ppp_t *ppp) +{ + struct cs_pd_t *pd; + + if (!conf_gw_ip_address) + return NULL; + + pd = find_pd(ppp); + + if (!pd) + return NULL; + + if (!pd->ip.addr) + return NULL; + + return &pd->ip; +} + +static char* get_passwd(struct pwdb_t *pwdb, struct ppp_t *ppp, const char *username) +{ + struct cs_pd_t *pd = find_pd(ppp); + + if (!pd) + pd = create_pd(ppp, username); + + if (!pd) + return NULL; + + return _strdup(pd->passwd); +} + +static struct ipdb_t ipdb = { + .get = get_ip, +}; + +static struct pwdb_t pwdb = { + .get_passwd = get_passwd, +}; + +static void load_config(void) +{ + const char *opt; + + if (conf_chap_secrets && conf_chap_secrets != def_chap_secrets) + _free(conf_chap_secrets); + opt = conf_get_opt("chap-secrets", "chap-secrets"); + if (opt) + conf_chap_secrets = _strdup(opt); + else + conf_chap_secrets = def_chap_secrets; + + opt = conf_get_opt("chap-secrets", "gw-ip-address"); + if (opt) + conf_gw_ip_address = inet_addr(opt); +} + +static void __init init(void) +{ + load_config(); + + pwdb_register(&pwdb); + ipdb_register(&ipdb); + + triton_event_register_handler(EV_PPP_FINISHED, (triton_event_func)ev_ppp_finished); + triton_event_register_handler(EV_PPP_PRE_UP, (triton_event_func)ev_ppp_pre_up); + triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); +} + diff --git a/accel-pppd/extra/ippool.c b/accel-pppd/extra/ippool.c new file mode 100644 index 00000000..6136a14d --- /dev/null +++ b/accel-pppd/extra/ippool.c @@ -0,0 +1,208 @@ +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#include <arpa/inet.h> + +#include "ipdb.h" +#include "list.h" +#include "spinlock.h" + +#include "memdebug.h" + +struct ippool_item_t +{ + struct list_head entry; + struct ipdb_item_t it; +}; + +struct ipaddr_t +{ + struct list_head entry; + in_addr_t addr; +}; + +static LIST_HEAD(gw_list); +static LIST_HEAD(tunnel_list); +static LIST_HEAD(ippool); +static spinlock_t pool_lock = SPINLOCK_INITIALIZER; +static struct ipdb_t ipdb; + +static in_addr_t conf_gw_ip_address; +static int cnt; + +static void parse_gw_ip_address(const char *val) +{ + if (!val) + return; + + conf_gw_ip_address = inet_addr(val); +} + +//parses ranges like x.x.x.x/mask +static int parse1(const char *str, uint32_t *begin, uint32_t *end) +{ + int n, f1, f2, f3, f4, m, mask = 0; + + n = sscanf(str, "%u.%u.%u.%u/%u",&f1, &f2, &f3, &f4, &m); + if (n != 5) + return -1; + if (f1 > 255) + return -1; + if (f2 > 255) + return -1; + if (f3 > 255) + return -1; + if (f4 > 255) + return -1; + if (m == 0 || m > 32) + return -1; + + for (n = 0; n < m ; n++) + mask |= 1 << n; + + *begin = (f4 << 24) | (f3 << 16) | (f2 << 8) | f1; + *end = *begin | ~mask; + + return 0; +} + +//parses ranges like x.x.x.x-y +static int parse2(const char *str, uint32_t *begin, uint32_t *end) +{ + int n, f1, f2, f3, f4, m; + + n = sscanf(str, "%u.%u.%u.%u-%u",&f1, &f2, &f3, &f4, &m); + if (n != 5) + return -1; + if (f1 > 255) + return -1; + if (f2 > 255) + return -1; + if (f3 > 255) + return -1; + if (f4 > 255) + return -1; + if (m < f4 || m > 255) + return -1; + + *begin = (f4 << 24) | (f3 << 16) | (f2 << 8) | f1; + *end = (m << 24) | (f3 << 16) | (f2 << 8) | f1; + + return 0; +} + +static void add_range(struct list_head *list, const char *name) +{ + uint32_t i,startip, endip; + struct ipaddr_t *ip; + + if (parse1(name, &startip, &endip)) + if (parse2(name, &startip, &endip)) { + fprintf(stderr, "ippool: cann't parse '%s'\n", name); + _exit(EXIT_FAILURE); + } + + for (i = ntohl(startip); i <= ntohl(endip); i++) { + ip = malloc(sizeof(*ip)); + ip->addr = htonl(i); + list_add_tail(&ip->entry, list); + cnt++; + } +} + +static void generate_pool(void) +{ + struct ippool_item_t *it; + struct ipaddr_t *addr = NULL; + struct ipaddr_t *peer_addr; + + while (1) { + if (list_empty(&tunnel_list)) + break; + else { + peer_addr = list_entry(tunnel_list.next, typeof(*peer_addr), entry); + list_del(&peer_addr->entry); + } + + if (!conf_gw_ip_address) { + if (list_empty(&gw_list)) + break; + else { + addr = list_entry(gw_list.next, typeof(*addr), entry); + list_del(&addr->entry); + } + } + + it = malloc(sizeof(*it)); + if (!it) { + fprintf(stderr, "ippool: out of memory\n"); + break; + } + + it->it.owner = &ipdb; + if (conf_gw_ip_address) + it->it.addr = conf_gw_ip_address; + else + it->it.addr = addr->addr; + + it->it.peer_addr = peer_addr->addr; + + list_add_tail(&it->entry, &ippool); + } +} + +static struct ipdb_item_t *get_ip(struct ppp_t *ppp) +{ + struct ippool_item_t *it; + + spin_lock(&pool_lock); + if (!list_empty(&ippool)) { + it = list_entry(ippool.next, typeof(*it), entry); + list_del(&it->entry); + } else + it = NULL; + spin_unlock(&pool_lock); + + return it ? &it->it : NULL; +} + +static void put_ip(struct ppp_t *ppp, struct ipdb_item_t *it) +{ + struct ippool_item_t *pit = container_of(it, typeof(*pit), it); + + spin_lock(&pool_lock); + list_add_tail(&pit->entry, &ippool); + spin_unlock(&pool_lock); +} + +static struct ipdb_t ipdb = { + .get = get_ip, + .put = put_ip, +}; + +static void __init ipool_init(void) +{ + struct conf_sect_t *s = conf_get_section("ip-pool"); + struct conf_option_t *opt; + + if (!s) + return; + + list_for_each_entry(opt, &s->items, entry) { + if (!strcmp(opt->name, "gw-ip-address")) + parse_gw_ip_address(opt->val); + else if (!strcmp(opt->name, "gw")) + add_range(&gw_list, opt->val); + else if (!strcmp(opt->name, "tunnel")) + add_range(&tunnel_list, opt->val); + else if (!opt->val) + add_range(&tunnel_list, opt->name); + } + + generate_pool(); + + ipdb_register(&ipdb); +} + diff --git a/accel-pppd/extra/pppd_compat.c b/accel-pppd/extra/pppd_compat.c new file mode 100644 index 00000000..f897dd68 --- /dev/null +++ b/accel-pppd/extra/pppd_compat.c @@ -0,0 +1,523 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <sched.h> +#include <limits.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include "linux_ppp.h" + +#include "triton.h" + +#include "events.h" +#include "ppp.h" +#include "log.h" +#include "utils.h" +#include "sigchld.h" + +#ifdef RADIUS +#include "radius.h" +#endif + +#include "memdebug.h" + +static char *conf_ip_up = "/etc/ppp/ip-up"; +static char *conf_ip_pre_up; +static char *conf_ip_down = "/etc/ppp/ip-down"; +static char *conf_ip_change; +static char *conf_radattr_prefix = "/var/run/radattr."; +static int conf_verbose = 0; + +static void *pd_key; + +struct pppd_compat_pd_t +{ + struct ppp_pd_t pd; + struct ppp_t *ppp; + struct sigchld_handler_t ip_pre_up_hnd; + struct sigchld_handler_t ip_up_hnd; + struct sigchld_handler_t ip_change_hnd; + struct sigchld_handler_t ip_down_hnd; +#ifdef RADIUS + int radattr_saved:1; +#endif + int started:1; + int res; + int bytes_sent; + int bytes_rcvd; +}; + +static struct pppd_compat_pd_t *find_pd(struct ppp_t *ppp); +static void fill_argv(char **argv, struct ppp_t *ppp, char *path); +static void fill_env(char **env, struct pppd_compat_pd_t *pd); +#ifdef RADIUS +static void remove_radattr(struct ppp_t *ppp); +static void write_radattr(struct ppp_t *ppp, struct rad_packet_t *pack, int save_old); +#endif + +static void ip_pre_up_handler(struct sigchld_handler_t *h, int status) +{ + struct pppd_compat_pd_t *pd = container_of(h, typeof(*pd), ip_pre_up_hnd); + if (conf_verbose) { + log_switch(NULL, pd->ppp); + log_ppp_info2("pppd_compat: ip-pre-up finished (%i)\n", status); + pd->res = status; + } + sched_yield(); + triton_context_wakeup(pd->ppp->ctrl->ctx); +} + +static void ip_up_handler(struct sigchld_handler_t *h, int status) +{ + struct pppd_compat_pd_t *pd = container_of(h, typeof(*pd), ip_up_hnd); + if (conf_verbose) { + log_switch(NULL, pd->ppp); + log_ppp_info2("pppd_compat: ip-up finished (%i)\n", status); + } +} + +static void ip_down_handler(struct sigchld_handler_t *h, int status) +{ + struct pppd_compat_pd_t *pd = container_of(h, typeof(*pd), ip_down_hnd); + if (conf_verbose) { + log_switch(NULL, pd->ppp); + log_ppp_info2("pppd_compat: ip-down finished (%i)\n", status); + } + sched_yield(); + triton_context_wakeup(pd->ppp->ctrl->ctx); +} + +static void ip_change_handler(struct sigchld_handler_t *h, int status) +{ + struct pppd_compat_pd_t *pd = container_of(h, typeof(*pd), ip_change_hnd); + if (conf_verbose) { + log_switch(NULL, pd->ppp); + log_ppp_info2("pppd_compat: ip-change finished (%i)\n", status); + } + sched_yield(); + pd->res = status; + triton_context_wakeup(pd->ppp->ctrl->ctx); +} + +static void ev_ppp_starting(struct ppp_t *ppp) +{ + struct pppd_compat_pd_t *pd = _malloc(sizeof(*pd)); + + if (!pd) { + log_emerg("pppd_compat: out of memory\n"); + return; + } + + memset(pd, 0, sizeof(*pd)); + pd->pd.key = &pd_key; + pd->ppp = ppp; + pd->ip_pre_up_hnd.handler = ip_pre_up_handler; + pd->ip_up_hnd.handler = ip_up_handler; + pd->ip_down_hnd.handler = ip_down_handler; + pd->ip_change_hnd.handler = ip_change_handler; + list_add_tail(&pd->pd.entry, &ppp->pd_list); +} + +static void ev_ppp_pre_up(struct ppp_t *ppp) +{ + pid_t pid; + char *argv[8]; + char *env[2]; + char ipaddr[17]; + char peer_ipaddr[17]; + char peername[64]; + struct pppd_compat_pd_t *pd = find_pd(ppp); + + if (!pd) + return; + + argv[4] = ipaddr; + argv[5] = peer_ipaddr; + fill_argv(argv, ppp, conf_ip_up); + + env[0] = peername; + env[1] = NULL; + fill_env(env, pd); + + if (conf_ip_pre_up) { + sigchld_lock(); + pid = fork(); + if (pid > 0) { + pd->ip_pre_up_hnd.pid = pid; + sigchld_register_handler(&pd->ip_pre_up_hnd); + if (conf_verbose) + log_ppp_info2("pppd_compat: ip-pre-up started (pid %i)\n", pid); + sigchld_unlock(); + triton_context_schedule(); + pthread_mutex_lock(&pd->ip_pre_up_hnd.lock); + pthread_mutex_unlock(&pd->ip_pre_up_hnd.lock); + if (pd->res != 0) { + ppp_terminate(ppp, 0, pd->res < 0 ? TERM_NAS_ERROR : TERM_ADMIN_RESET); + return; + } + } else if (pid == 0) { + execve(conf_ip_pre_up, argv, env); + log_emerg("pppd_compat: exec '%s': %s\n", conf_ip_pre_up, strerror(errno)); + _exit(EXIT_FAILURE); + } else + log_error("pppd_compat: fork: %s\n", strerror(errno)); + } +} + +static void ev_ppp_started(struct ppp_t *ppp) +{ + pid_t pid; + char *argv[8]; + char *env[2]; + char ipaddr[17]; + char peer_ipaddr[17]; + char peername[64]; + struct pppd_compat_pd_t *pd = find_pd(ppp); + + if (!pd) + return; + + argv[4] = ipaddr; + argv[5] = peer_ipaddr; + fill_argv(argv, ppp, conf_ip_up); + + env[0] = peername; + env[1] = NULL; + fill_env(env, pd); + + if (conf_ip_up) { + sigchld_lock(); + pid = fork(); + if (pid > 0) { + pd->ip_up_hnd.pid = pid; + sigchld_register_handler(&pd->ip_up_hnd); + if (conf_verbose) + log_ppp_info2("pppd_compat: ip-up started (pid %i)\n", pid); + sigchld_unlock(); + } else if (pid == 0) { + execve(conf_ip_up, argv, env); + log_emerg("pppd_compat: exec '%s': %s\n", conf_ip_up, strerror(errno)); + _exit(EXIT_FAILURE); + } else + log_error("pppd_compat: fork: %s\n", strerror(errno)); + } + + pd->started = 1; +} + +static void ev_ppp_finishing(struct ppp_t *ppp) +{ + struct ifpppstatsreq ifreq; + struct pppd_compat_pd_t *pd = find_pd(ppp); + + if (!pd) + return; + + memset(&ifreq, 0, sizeof(ifreq)); + ifreq.stats_ptr = (void *)&ifreq.stats; + strcpy(ifreq.ifr__name, ppp->ifname); + + if (ioctl(sock_fd, SIOCGPPPSTATS, &ifreq)) { + log_ppp_error("pppd_compat: failed to get ppp statistics: %s\n", strerror(errno)); + return; + } + + pd->bytes_sent = ifreq.stats.p.ppp_obytes; + pd->bytes_rcvd = ifreq.stats.p.ppp_ibytes; +} + +static void ev_ppp_finished(struct ppp_t *ppp) +{ + pid_t pid; + char *argv[8]; + char *env[5]; + char ipaddr[17]; + char peer_ipaddr[17]; + char peername[64]; + char connect_time[24]; + char bytes_sent[24]; + char bytes_rcvd[24]; + struct pppd_compat_pd_t *pd = find_pd(ppp); + + if (!pd) + return; + + if (!pd->started) + goto skip; + + pthread_mutex_lock(&pd->ip_up_hnd.lock); + if (pd->ip_up_hnd.pid) { + log_ppp_warn("pppd_compat: ip-up is not yet finished, terminating it ...\n"); + kill(pd->ip_up_hnd.pid, SIGTERM); + } + pthread_mutex_unlock(&pd->ip_up_hnd.lock); + + argv[4] = ipaddr; + argv[5] = peer_ipaddr; + fill_argv(argv, pd->ppp, conf_ip_down); + + env[0] = peername; + env[1] = connect_time; + env[2] = bytes_sent; + env[3] = bytes_rcvd; + env[4] = NULL; + fill_env(env, pd); + + sigchld_lock(); + pid = fork(); + if (pid > 0) { + pd->ip_down_hnd.pid = pid; + sigchld_register_handler(&pd->ip_down_hnd); + if (conf_verbose) + log_ppp_info2("pppd_compat: ip-down started (pid %i)\n", pid); + sigchld_unlock(); + triton_context_schedule(); + pthread_mutex_lock(&pd->ip_down_hnd.lock); + pthread_mutex_unlock(&pd->ip_down_hnd.lock); + } else if (pid == 0) { + execve(conf_ip_down, argv, env); + log_emerg("pppd_compat: exec '%s': %s\n", conf_ip_down, strerror(errno)); + _exit(EXIT_FAILURE); + } else + log_error("pppd_compat: fork: %s\n", strerror(errno)); + + pthread_mutex_lock(&pd->ip_up_hnd.lock); + if (pd->ip_up_hnd.pid) { + log_ppp_warn("pppd_compat: ip-up is not yet finished, killing it ...\n"); + kill(pd->ip_up_hnd.pid, SIGKILL); + pthread_mutex_unlock(&pd->ip_up_hnd.lock); + sigchld_unregister_handler(&pd->ip_up_hnd); + } else + pthread_mutex_unlock(&pd->ip_up_hnd.lock); + +skip: +#ifdef RADIUS + if (pd->radattr_saved) + remove_radattr(ppp); +#endif + + list_del(&pd->pd.entry); + _free(pd); +} + +#ifdef RADIUS +static void ev_radius_access_accept(struct ev_radius_t *ev) +{ + struct pppd_compat_pd_t *pd = find_pd(ev->ppp); + + write_radattr(ev->ppp, ev->reply, 0); + + pd->radattr_saved = 1; +} + +static void ev_radius_coa(struct ev_radius_t *ev) +{ + pid_t pid; + char *argv[8]; + char *env[2]; + char ipaddr[17]; + char peer_ipaddr[17]; + char peername[64]; + struct pppd_compat_pd_t *pd = find_pd(ev->ppp); + + if (!pd) + return; + + write_radattr(ev->ppp, ev->request, 1); + + argv[4] = ipaddr; + argv[5] = peer_ipaddr; + fill_argv(argv, pd->ppp, conf_ip_change); + + env[0] = peername; + env[1] = NULL; + fill_env(env, pd); + + sigchld_lock(); + pid = fork(); + if (pid > 0) { + pd->ip_change_hnd.pid = pid; + sigchld_register_handler(&pd->ip_change_hnd); + sigchld_unlock(); + if (conf_verbose) + log_ppp_info2("pppd_compat: ip-change started (pid %i)\n", pid); + triton_context_schedule(); + if (!ev->res) + ev->res = pd->res; + } else if (pid == 0) { + execve(conf_ip_change, argv, env); + log_emerg("pppd_compat: exec '%s': %s\n", conf_ip_change, strerror(errno)); + _exit(EXIT_FAILURE); + } else + log_error("pppd_compat: fork: %s\n", strerror(errno)); +} + +static void remove_radattr(struct ppp_t *ppp) +{ + char *fname; + + fname = _malloc(PATH_MAX); + if (!fname) { + log_emerg("pppd_compat: out of memory\n"); + return; + } + + sprintf(fname, "%s.%s", conf_radattr_prefix, ppp->ifname); + if (unlink(fname)) { + log_ppp_warn("pppd_compat: failed to remove '%s': %s\n", fname, strerror(errno)); + } + sprintf(fname, "%s_old.%s", conf_radattr_prefix, ppp->ifname); + unlink(fname); + + _free(fname); +} + +static void write_radattr(struct ppp_t *ppp, struct rad_packet_t *pack, int save_old) +{ + struct rad_attr_t *attr; + struct rad_dict_value_t *val; + FILE *f; + char *fname1, *fname2 = NULL; + int i; + + fname1 = _malloc(PATH_MAX); + if (!fname1) { + log_emerg("pppd_compat: out of memory\n"); + return; + } + + if (save_old) { + fname2 = _malloc(PATH_MAX); + if (!fname2) { + log_emerg("pppd_compat: out of memory\n"); + _free(fname1); + return; + } + } + + sprintf(fname1, "%s.%s", conf_radattr_prefix, ppp->ifname); + if (save_old) { + sprintf(fname2, "%s_old.%s", conf_radattr_prefix, ppp->ifname); + if (rename(fname1, fname2)) { + log_ppp_warn("pppd_compat: rename: %s\n", strerror(errno)); + } + } + + f = fopen(fname1, "w"); + if (f) { + list_for_each_entry(attr, &pack->attrs, entry) { + fprintf(f, "%s ", attr->attr->name); + switch (attr->attr->type) { + case ATTR_TYPE_INTEGER: + val = rad_dict_find_val(attr->attr, attr->val); + if (val) + fprintf(f, "%s\n", val->name); + else + fprintf(f, "%i\n", attr->val.integer); + break; + case ATTR_TYPE_STRING: + fprintf(f, "%s\n", attr->val.string); + break; + case ATTR_TYPE_OCTETS: + for (i = 0; i < attr->len; i++) + fprintf(f, "%02X", attr->val.octets[i]); + fprintf(f, "\n"); + break; + case ATTR_TYPE_IPADDR: + fprintf(f, "%i.%i.%i.%i\n", attr->val.ipaddr & 0xff, (attr->val.ipaddr >> 8) & 0xff, (attr->val.ipaddr >> 16) & 0xff, (attr->val.ipaddr >> 24) & 0xff); + break; + case ATTR_TYPE_DATE: + fprintf(f, "%lu\n", attr->val.date); + break; + } + } + fclose(f); + } else + log_ppp_warn("pppd_compat: failed to create '%s': %s\n", fname1, strerror(errno)); + + _free(fname1); + if (save_old) + _free(fname2); +} +#endif + +static struct pppd_compat_pd_t *find_pd(struct ppp_t *ppp) +{ + struct ppp_pd_t *pd; + struct pppd_compat_pd_t *cpd; + + list_for_each_entry(pd, &ppp->pd_list, entry) { + if (pd->key == &pd_key) { + cpd = container_of(pd, typeof(*cpd), pd); + return cpd; + } + } + + log_ppp_warn("pppd_compat: pd not found\n"); + return NULL; +} + +static void fill_argv(char **argv, struct ppp_t *ppp, char *path) +{ + argv[0] = path; + argv[1] = ppp->ifname; + argv[2] = "none"; + argv[3] = "0"; + u_inet_ntoa(ppp->ipaddr, argv[4]); + u_inet_ntoa(ppp->peer_ipaddr, argv[5]); + argv[6] = ppp->ctrl->calling_station_id; + argv[7] = NULL; +} + +static void fill_env(char **env, struct pppd_compat_pd_t *pd) +{ + snprintf(env[0], 64, "PEERNAME=%s", pd->ppp->username); + + if (pd->ppp->stop_time && env[1]) { + snprintf(env[1], 24, "CONNECT_TIME=%lu", pd->ppp->stop_time - pd->ppp->start_time); + snprintf(env[2], 24, "BYTES_SENT=%u", pd->bytes_sent); + snprintf(env[3], 24, "BYTES_RCVD=%u", pd->bytes_rcvd); + } +} + +static void __init init(void) +{ + char *opt; + + opt = conf_get_opt("pppd-compat", "ip-pre-up"); + if (opt) + conf_ip_pre_up = _strdup(opt); + + opt = conf_get_opt("pppd-compat", "ip-up"); + if (opt) + conf_ip_up = _strdup(opt); + + opt = conf_get_opt("pppd-compat", "ip-down"); + if (opt) + conf_ip_down = _strdup(opt); + + opt = conf_get_opt("pppd-compat", "ip-change"); + if (opt) + conf_ip_change = _strdup(opt); + + opt = conf_get_opt("pppd-compat", "radattr-prefix"); + if (opt) + conf_radattr_prefix = _strdup(opt); + + opt = conf_get_opt("pppd-compat", "verbose"); + if (opt && atoi(opt) > 0) + conf_verbose = 1; + + triton_event_register_handler(EV_PPP_STARTING, (triton_event_func)ev_ppp_starting); + triton_event_register_handler(EV_PPP_PRE_UP, (triton_event_func)ev_ppp_pre_up); + triton_event_register_handler(EV_PPP_STARTED, (triton_event_func)ev_ppp_started); + triton_event_register_handler(EV_PPP_FINISHING, (triton_event_func)ev_ppp_finishing); + triton_event_register_handler(EV_PPP_FINISHED, (triton_event_func)ev_ppp_finished); +#ifdef RADIUS + triton_event_register_handler(EV_RADIUS_ACCESS_ACCEPT, (triton_event_func)ev_radius_access_accept); + triton_event_register_handler(EV_RADIUS_COA, (triton_event_func)ev_radius_coa); +#endif +} diff --git a/accel-pppd/extra/shaper_tbf.c b/accel-pppd/extra/shaper_tbf.c new file mode 100644 index 00000000..d7e4f0bd --- /dev/null +++ b/accel-pppd/extra/shaper_tbf.c @@ -0,0 +1,1274 @@ +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <netinet/in.h> +#include <linux/if.h> +#include <linux/if_ether.h> +#include <linux/pkt_cls.h> +#include <linux/pkt_sched.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <pthread.h> + +#include "netlink/netlink.h" +#include "netlink/socket.h" +#include "netlink/msg.h" + +#include "triton.h" +#include "events.h" +#include "log.h" +#include "ppp.h" +#include "cli.h" + +#ifdef RADIUS +#include "radius.h" +#endif + +#include "memdebug.h" + +#define RTNL_TC_RTABLE_SIZE 256 +#define TIME_UNITS_PER_SEC 1000000 + +#define ATTR_UP 1 +#define ATTR_DOWN 2 + +static int conf_verbose = 0; +#ifdef RADIUS +static int conf_attr_down = 11; //Filter-Id +static int conf_attr_up = 11; //Filter-Id +static int conf_vendor = 0; +#endif +static double conf_down_burst_factor = 0.1; +static double conf_up_burst_factor = 1; +static int conf_latency = 50; +static int conf_mpu = 0; + +static int temp_down_speed; +static int temp_up_speed; + +static pthread_rwlock_t shaper_lock = PTHREAD_RWLOCK_INITIALIZER; +static LIST_HEAD(shaper_list); + +static double tick_in_usec = 1; +static double clock_factor = 1; + +struct time_range_pd_t; +struct shaper_pd_t +{ + struct list_head entry; + struct ppp_t *ppp; + struct ppp_pd_t pd; + int temp_down_speed; + int temp_up_speed; + int down_speed; + int up_speed; + struct list_head tr_list; + struct time_range_pd_t *cur_tr; +}; + +struct time_range_pd_t +{ + struct list_head entry; + int id; + int down_speed; + int down_burst; + int up_speed; + int up_burst; +}; + +struct time_range_t +{ + struct list_head entry; + int id; + struct triton_timer_t begin; + struct triton_timer_t end; +}; + +static void *pd_key; + +static LIST_HEAD(time_range_list); +static int time_range_id; + +static void shaper_ctx_close(struct triton_context_t *); +static struct triton_context_t shaper_ctx = { + .close = shaper_ctx_close, + .before_switch = log_switch, +}; + +static unsigned tc_time2tick(unsigned time) +{ + return time*tick_in_usec; +} + +/*static unsigned tc_tick2time(unsigned tick) +{ + return tick/tick_in_usec; +}*/ + +static unsigned tc_calc_xmittime(unsigned rate, unsigned size) +{ + return tc_time2tick(TIME_UNITS_PER_SEC*((double)size/rate)); +} + +/*static unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks) +{ + return ((double)rate*tc_tick2time(ticks))/TIME_UNITS_PER_SEC; +}*/ + +static void tc_calc_rtable(struct tc_ratespec *r, uint32_t *rtab, int cell_log, unsigned mtu) +{ + int i; + unsigned sz; + unsigned bps = r->rate; + unsigned mpu = r->mpu; + + if (mtu == 0) + mtu = 2047; + + if (cell_log <= 0) { + cell_log = 0; + while ((mtu >> cell_log) > 255) + cell_log++; + } + + for (i=0; i<256; i++) { + //sz = tc_adjust_size((i + 1) << cell_log, mpu, linklayer); + sz = (i + 1) << cell_log; + if (sz < mpu) + sz = mpu; + rtab[i] = tc_calc_xmittime(bps, sz); + } + + r->cell_align=-1; // Due to the sz calc + r->cell_log=cell_log; +} + +static int install_tbf(struct nl_sock *h, int ifindex, int speed, int burst) +{ + struct tc_tbf_qopt opt; + struct nl_msg *msg; + struct nl_msg *pmsg = NULL; + uint32_t rtab[RTNL_TC_RTABLE_SIZE]; + double rate = speed * 1000 / 8; + double bucket = burst ? burst : rate * conf_down_burst_factor; + + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = ifindex, + .tcm_handle = 0x00010000, + .tcm_parent = TC_H_ROOT, + }; + + memset(&opt, 0, sizeof(opt)); + + opt.rate.rate = rate; + opt.rate.mpu = conf_mpu; + opt.limit = rate*conf_latency/1000 + bucket; + opt.buffer = tc_calc_xmittime(rate, bucket); + + tc_calc_rtable(&opt.rate, rtab, 0, 0); + + msg = nlmsg_alloc(); + if (!msg) + goto out_err; + + NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opt), &opt); + NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab); + + pmsg = nlmsg_alloc_simple(RTM_NEWQDISC, NLM_F_CREATE | NLM_F_REPLACE); + if (!pmsg) + goto out_err; + + if (nlmsg_append(pmsg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) + goto out_err; + + NLA_PUT_STRING(pmsg, TCA_KIND, "tbf"); + nla_put_nested(pmsg, TCA_OPTIONS, msg); + + if (nl_send_auto_complete(h, pmsg) < 0) + goto out_err; + + if (nl_wait_for_ack(h) < 0) + goto out_err; + + nlmsg_free(msg); + nlmsg_free(pmsg); + + return 0; + +out_err: +nla_put_failure: + + if (msg) + nlmsg_free(msg); + + if (pmsg) + nlmsg_free(pmsg); + + log_ppp_error("tbf: error occured, tbf is not installed\n"); + + return -1; +} + +static int install_ingress(struct nl_sock *h, int ifindex) +{ + struct nl_msg *pmsg; + + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = ifindex, + .tcm_handle = 0xffff0000, + .tcm_parent = TC_H_INGRESS, + }; + + pmsg = nlmsg_alloc_simple(RTM_NEWQDISC, NLM_F_CREATE | NLM_F_REPLACE); + if (!pmsg) + goto out_err; + + if (nlmsg_append(pmsg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) + goto out_err; + + NLA_PUT_STRING(pmsg, TCA_KIND, "ingress"); + + if (nl_send_auto_complete(h, pmsg) < 0) + goto out_err; + + if (nl_wait_for_ack(h) < 0) + goto out_err; + + nlmsg_free(pmsg); + + return 0; + +out_err: +nla_put_failure: + + if (pmsg) + nlmsg_free(pmsg); + + log_ppp_error("tbf: error occured, ingress is not installed\n"); + + return -1; +} + +static int install_filter(struct nl_sock *h, int ifindex, int speed, int burst) +{ + //double rate = speed*1000/8; + //double bucket = rate*conf_burst_factor; + double rate = speed * 1000 / 8; + double bucket = burst ? burst : rate * conf_up_burst_factor; + struct nl_msg *pmsg = NULL; + struct nl_msg *msg = NULL; + struct nl_msg *msg1 = NULL; + struct nl_msg *msg2 = NULL; + struct nl_msg *msg3 = NULL; + uint32_t rtab[RTNL_TC_RTABLE_SIZE]; + + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = ifindex, + .tcm_handle = 1, + .tcm_parent = 0xffff0000, + .tcm_info = TC_H_MAKE(10 << 16, ntohs(ETH_P_IP)), + }; + + struct sel_t { + struct tc_u32_sel sel; + struct tc_u32_key key; + } sel = { + .sel.nkeys = 1, + .sel.flags = TC_U32_TERMINAL, + .key.off = 12, + }; + + struct tc_police police = { + .action = TC_POLICE_SHOT, + .rate.rate = rate, + .rate.mpu = conf_mpu, + .limit = rate*conf_latency/1000 + bucket, + .burst = tc_calc_xmittime(rate, bucket), + }; + + tc_calc_rtable(&police.rate, rtab, 0, 0); + + pmsg = nlmsg_alloc_simple(RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_REPLACE); + if (!pmsg) + goto out_err; + + msg = nlmsg_alloc(); + if (!msg) + goto out_err; + + msg1 = nlmsg_alloc(); + if (!msg1) + goto out_err; + + msg2 = nlmsg_alloc(); + if (!msg2) + goto out_err; + + msg3 = nlmsg_alloc(); + if (!msg3) + goto out_err; + + if (nlmsg_append(pmsg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) + goto out_err; + + NLA_PUT_STRING(pmsg, TCA_KIND, "u32"); + + NLA_PUT_U32(msg, TCA_U32_CLASSID, 1); + NLA_PUT(msg, TCA_U32_SEL, sizeof(sel), &sel); + + NLA_PUT_STRING(msg3, TCA_ACT_KIND, "police"); + + NLA_PUT(msg2, TCA_POLICE_TBF, sizeof(police), &police); + NLA_PUT(msg2, TCA_POLICE_RATE, sizeof(rtab), rtab); + + if (nla_put_nested(msg3, TCA_ACT_OPTIONS, msg2) < 0) + goto out_err; + + if (nla_put_nested(msg1, 1, msg3) < 0) + goto out_err; + + if (nla_put_nested(msg, TCA_U32_ACT, msg1)) + goto out_err; + + if (nla_put_nested(pmsg, TCA_OPTIONS, msg)) + goto out_err; + + if (nl_send_auto_complete(h, pmsg) < 0) + goto out_err; + + if (nl_wait_for_ack(h) < 0) + goto out_err; + + nlmsg_free(pmsg); + nlmsg_free(msg); + nlmsg_free(msg1); + nlmsg_free(msg2); + nlmsg_free(msg3); + + return 0; + +out_err: +nla_put_failure: + + if (pmsg) + nlmsg_free(pmsg); + + if (msg) + nlmsg_free(msg); + + if (msg1) + nlmsg_free(msg1); + + if (msg2) + nlmsg_free(msg1); + + if (msg3) + nlmsg_free(msg1); + + log_ppp_error("tbf: error occured, filter is not installed\n"); + + return -1; +} + + +static int install_shaper(const char *ifname, int down_speed, int down_burst, int up_speed, int up_burst) +{ + struct nl_sock *h; + struct ifreq ifr; + int err; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, ifname); + + if (ioctl(sock_fd, SIOCGIFINDEX, &ifr)) { + log_ppp_error("tbf: ioctl(SIOCGIFINDEX)", strerror(errno)); + return -1; + } + + h = nl_socket_alloc(); + if (!h) { + log_ppp_error("tbf: nl_socket_alloc failed\n"); + return -1; + } + + err = nl_connect(h, NETLINK_ROUTE); + if (err < 0) { + log_ppp_error("tbf: nl_connect: %s", strerror(errno)); + goto out; + } + + if (down_speed) + if (install_tbf(h, ifr.ifr_ifindex, down_speed, down_burst)) + return -1; + + if (up_speed) { + if (install_ingress(h, ifr.ifr_ifindex)) + return -1; + if (install_filter(h, ifr.ifr_ifindex, up_speed, up_burst)) + return -1; + } + + nl_close(h); +out: + nl_socket_free(h); + + return 0; +} + +static struct shaper_pd_t *find_pd(struct ppp_t *ppp, int create) +{ + struct ppp_pd_t *pd; + struct shaper_pd_t *spd; + + list_for_each_entry(pd, &ppp->pd_list, entry) { + if (pd->key == &pd_key) { + spd = container_of(pd, typeof(*spd), pd); + return spd; + } + } + + if (create) { + spd = _malloc(sizeof(*spd)); + if (!spd) { + log_emerg("tbf: out of memory\n"); + return NULL; + } + + memset(spd, 0, sizeof(*spd)); + spd->ppp = ppp; + list_add_tail(&spd->pd.entry, &ppp->pd_list); + spd->pd.key = &pd_key; + INIT_LIST_HEAD(&spd->tr_list); + + pthread_rwlock_wrlock(&shaper_lock); + list_add_tail(&spd->entry, &shaper_list); + pthread_rwlock_unlock(&shaper_lock); + return spd; + } + + return NULL; +} + +static int remove_shaper(const char *ifname) +{ + struct nl_sock *h; + struct ifreq ifr; + struct nl_msg *pmsg; + int err; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, ifname); + + if (ioctl(sock_fd, SIOCGIFINDEX, &ifr)) { + log_ppp_error("tbf: ioctl(SIOCGIFINDEX)", strerror(errno)); + return -1; + } + + struct tcmsg tchdr1 = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = ifr.ifr_ifindex, + .tcm_handle = 0x00010000, + .tcm_parent = TC_H_ROOT, + }; + + struct tcmsg tchdr2 = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = ifr.ifr_ifindex, + .tcm_handle = 0xffff0000, + .tcm_parent = TC_H_INGRESS, + }; + + h = nl_socket_alloc(); + if (!h) { + log_ppp_error("tbf: nl_socket_alloc failed\n"); + return -1; + } + + err = nl_connect(h, NETLINK_ROUTE); + if (err < 0) { + log_ppp_error("tbf: nl_connect: %s", strerror(errno)); + nl_socket_free(h); + return -1; + } + + pmsg = nlmsg_alloc_simple(RTM_DELQDISC, NLM_F_CREATE | NLM_F_REPLACE); + if (!pmsg) + goto out_err; + + if (nlmsg_append(pmsg, &tchdr1, sizeof(tchdr1), NLMSG_ALIGNTO) < 0) + goto out_err; + + if (nl_send_auto_complete(h, pmsg) < 0) + goto out_err; + + if (nl_wait_for_ack(h) < 0) + goto out_err; + + nlmsg_free(pmsg); + + pmsg = nlmsg_alloc_simple(RTM_DELQDISC, NLM_F_CREATE | NLM_F_REPLACE); + if (!pmsg) + goto out_err; + + if (nlmsg_append(pmsg, &tchdr2, sizeof(tchdr2), NLMSG_ALIGNTO) < 0) + goto out_err; + + if (nl_send_auto_complete(h, pmsg) < 0) + goto out_err; + + if (nl_wait_for_ack(h) < 0) + goto out_err; + + nlmsg_free(pmsg); + + nl_close(h); + nl_socket_free(h); + return 0; + +out_err: + log_ppp_error("tbf: failed to remove shaper\n"); + + if (pmsg) + nlmsg_free(pmsg); + + nl_close(h); + nl_socket_free(h); + + return -1; +} + +static void parse_string(const char *str, int dir, int *speed, int *burst, int *tr_id) +{ + char *endptr; + long int val; + unsigned int n1, n2, n3; + + if (strstr(str, "lcp:interface-config#1=rate-limit output access-group") == str) { + if (dir == ATTR_DOWN) { + val = sscanf(str, "lcp:interface-config#1=rate-limit output access-group %i %u %u %u conform-action transmit exceed-action drop", tr_id, &n1, &n2, &n3); + if (val == 4) { + *speed = n1/1000; + *burst = n2; + } + } + return; + } else if (strstr(str, "lcp:interface-config#1=rate-limit input access-group") == str) { + if (dir == ATTR_UP) { + val = sscanf(str, "lcp:interface-config#1=rate-limit input access-group %i %u %u %u conform-action transmit exceed-action drop", tr_id, &n1, &n2, &n3); + if (val == 4) { + *speed = n1/1000; + *burst = n2; + } + } + return; + } else if (strstr(str, "lcp:interface-config#1=rate-limit output") == str) { + if (dir == ATTR_DOWN) { + val = sscanf(str, "lcp:interface-config#1=rate-limit output %u %u %u conform-action transmit exceed-action drop", &n1, &n2, &n3); + if (val == 3) { + *speed = n1/1000; + *burst = n2; + } + } + return; + } else if (strstr(str, "lcp:interface-config#1=rate-limit input") == str) { + if (dir == ATTR_UP) { + val = sscanf(str, "lcp:interface-config#1=rate-limit input %u %u %u conform-action transmit exceed-action drop", &n1, &n2, &n3); + if (val == 3) { + *speed = n1/1000; + *burst = n2; + } + } + return; + } + + val = strtol(str, &endptr, 10); + if (*endptr == 0) { + *speed = val; + return; + } + if (*endptr == ',') { + *tr_id = val; + val = strtol(endptr + 1, &endptr, 10); + } + if (*endptr == 0) { + *speed = val; + return; + } else { + if (*endptr == '/' || *endptr == '\\' || *endptr == ':') { + if (dir == ATTR_DOWN) + *speed = val; + else + *speed = strtol(endptr + 1, &endptr, 10); + } + } +} + +#ifdef RADIUS +static void parse_attr(struct rad_attr_t *attr, int dir, int *speed, int *burst, int *tr_id) +{ + if (attr->attr->type == ATTR_TYPE_STRING) + parse_string(attr->val.string, dir, speed, burst, tr_id); + else if (attr->attr->type == ATTR_TYPE_INTEGER) + *speed = attr->val.integer; +} + +static struct time_range_pd_t *get_tr_pd(struct shaper_pd_t *pd, int id) +{ + struct time_range_pd_t *tr_pd; + + list_for_each_entry(tr_pd, &pd->tr_list, entry) { + if (tr_pd->id == id) + return tr_pd; + } + + tr_pd = _malloc(sizeof(*tr_pd)); + memset(tr_pd, 0, sizeof(*tr_pd)); + tr_pd->id = id; + + if (id == time_range_id) + pd->cur_tr = tr_pd; + + list_add_tail(&tr_pd->entry, &pd->tr_list); + + return tr_pd; +} + +static void clear_tr_pd(struct shaper_pd_t *pd) +{ + struct time_range_pd_t *tr_pd; + + while (!list_empty(&pd->tr_list)) { + tr_pd = list_entry(pd->tr_list.next, typeof(*tr_pd), entry); + list_del(&tr_pd->entry); + _free(tr_pd); + } +} + +static void check_radius_attrs(struct shaper_pd_t *pd, struct rad_packet_t *pack) +{ + struct rad_attr_t *attr; + int down_speed, down_burst; + int up_speed, up_burst; + int tr_id; + struct time_range_pd_t *tr_pd; + + list_for_each_entry(attr, &pack->attrs, entry) { + if (attr->vendor && attr->vendor->id != conf_vendor) + continue; + if (!attr->vendor && conf_vendor) + continue; + if (attr->attr->id != conf_attr_down && attr->attr->id != conf_attr_up) + continue; + tr_id = 0; + down_speed = 0; + down_burst = 0; + up_speed = 0; + up_burst = 0; + if (attr->attr->id == conf_attr_down) + parse_attr(attr, ATTR_DOWN, &down_speed, &down_burst, &tr_id); + if (attr->attr->id == conf_attr_up) + parse_attr(attr, ATTR_UP, &up_speed, &up_burst, &tr_id); + tr_pd = get_tr_pd(pd, tr_id); + if (down_speed) + tr_pd->down_speed = down_speed; + if (down_burst) + tr_pd->down_burst = down_burst; + if (up_speed) + tr_pd->up_speed = up_speed; + if (up_burst) + tr_pd->up_burst = up_burst; + } +} + +static void ev_radius_access_accept(struct ev_radius_t *ev) +{ + int down_speed, down_burst; + int up_speed, up_burst; + struct shaper_pd_t *pd = find_pd(ev->ppp, 1); + + if (!pd) + return; + + check_radius_attrs(pd, ev->reply); + + if (temp_down_speed || temp_up_speed) { + pd->temp_down_speed = temp_down_speed; + pd->temp_up_speed = temp_up_speed; + pd->down_speed = temp_down_speed; + pd->up_speed = temp_up_speed; + down_speed = temp_down_speed; + up_speed = temp_up_speed; + down_burst = 0; + up_burst = 0; + } else { + if (!pd->cur_tr) + return; + pd->down_speed = pd->cur_tr->down_speed; + pd->up_speed = pd->cur_tr->up_speed; + down_speed = pd->cur_tr->down_speed; + up_speed = pd->cur_tr->up_speed; + down_burst = pd->cur_tr->down_burst; + up_burst = pd->cur_tr->up_speed; + } + + if (down_speed > 0 && up_speed > 0) { + if (!install_shaper(ev->ppp->ifname, down_speed, down_burst, up_speed, up_burst)) { + if (conf_verbose) + log_ppp_info2("tbf: installed shaper %i/%i (Kbit)\n", down_speed, up_speed); + } + } +} + +static void ev_radius_coa(struct ev_radius_t *ev) +{ + struct shaper_pd_t *pd = find_pd(ev->ppp, 0); + + if (!pd) { + ev->res = -1; + return; + } + + clear_tr_pd(pd); + check_radius_attrs(pd, ev->request); + + if (pd->temp_down_speed || pd->temp_up_speed) + return; + + if (!pd->cur_tr) { + if (pd->down_speed || pd->up_speed) { + pd->down_speed = 0; + pd->up_speed = 0; + if (conf_verbose) + log_ppp_info2("tbf: removed shaper\n"); + remove_shaper(ev->ppp->ifname); + } + return; + } + + if (pd->down_speed != pd->cur_tr->down_speed || pd->up_speed != pd->cur_tr->up_speed) { + pd->down_speed = pd->cur_tr->down_speed; + pd->up_speed = pd->cur_tr->up_speed; + + if (remove_shaper(ev->ppp->ifname)) { + ev->res = -1; + return; + } + + if (pd->down_speed > 0 || pd->up_speed > 0) { + if (install_shaper(ev->ppp->ifname, pd->cur_tr->down_speed, pd->cur_tr->down_burst, pd->cur_tr->up_speed, pd->cur_tr->up_burst)) { + ev->res= -1; + return; + } else { + if (conf_verbose) + log_ppp_info2("tbf: changed shaper %i/%i (Kbit)\n", pd->down_speed, pd->up_speed); + } + } else { + if (conf_verbose) + log_ppp_info2("tbf: removed shaper\n"); + } + } +} +#endif + +static void ev_shaper(struct ev_shaper_t *ev) +{ + struct shaper_pd_t *pd = find_pd(ev->ppp, 1); + int down_speed = 0, down_burst = 0; + int up_speed = 0, up_burst = 0; + int tr_id; + + if (!pd) + return; + + parse_string(ev->val, ATTR_DOWN, &down_speed, &down_burst, &tr_id); + parse_string(ev->val, ATTR_UP, &up_speed, &up_burst, &tr_id); + + if (down_speed > 0 && up_speed > 0) { + pd->down_speed = down_speed; + pd->up_speed = up_speed; + if (!install_shaper(ev->ppp->ifname, down_speed, down_burst, up_speed, up_burst)) { + if (conf_verbose) + log_ppp_info2("tbf: installed shaper %i/%i (Kbit)\n", down_speed, up_speed); + } + } +} + +static void ev_ctrl_finished(struct ppp_t *ppp) +{ + struct shaper_pd_t *pd = find_pd(ppp, 0); + + if (pd) { + clear_tr_pd(pd); + pthread_rwlock_wrlock(&shaper_lock); + list_del(&pd->entry); + pthread_rwlock_unlock(&shaper_lock); + list_del(&pd->pd.entry); + _free(pd); + } +} + +static void shaper_change_help(char * const *f, int f_cnt, void *cli) +{ + cli_send(cli, "shaper change <interface> <value> [temp] - change shaper on specified interface, if temp is set then previous settings may be restored later by 'shaper restore'\r\n"); + cli_send(cli, "shaper change all <value> [temp] - change shaper on all interfaces, if temp is set also new interfaces will have specified shaper value\r\n"); +} + +static void shaper_change(struct shaper_pd_t *pd) +{ + if ((pd->temp_down_speed && pd->temp_up_speed) || (pd->down_speed && pd->up_speed)) + remove_shaper(pd->ppp->ifname); + + if (pd->temp_down_speed && pd->temp_up_speed) + install_shaper(pd->ppp->ifname, pd->temp_down_speed, 0, pd->temp_up_speed, 0); + else if (pd->down_speed && pd->up_speed) + install_shaper(pd->ppp->ifname, pd->cur_tr->down_speed, pd->cur_tr->down_burst, pd->cur_tr->up_speed, pd->cur_tr->up_burst); +} + +static int shaper_change_exec(const char *cmd, char * const *f, int f_cnt, void *cli) +{ + struct shaper_pd_t *pd; + int down_speed = 0, up_speed = 0, down_burst = 0, up_burst = 0; + int all = 0, temp = 0, found = 0; + int tr_id; + + if (f_cnt < 4) + return CLI_CMD_SYNTAX; + + parse_string(f[3], ATTR_DOWN, &down_speed, &down_burst, &tr_id); + parse_string(f[3], ATTR_UP, &up_speed, &up_burst, &tr_id); + + if (down_speed == 0 || up_speed == 0) + return CLI_CMD_INVAL; + + if (!strcmp(f[2], "all")) + all = 1; + + if (f_cnt == 5) { + if (strcmp(f[4], "temp")) + return CLI_CMD_SYNTAX; + else + temp = 1; + } + + if (all && temp) { + temp_down_speed = down_speed; + temp_up_speed = up_speed; + } + + pthread_rwlock_rdlock(&shaper_lock); + list_for_each_entry(pd, &shaper_list, entry) { + if (all || !strcmp(f[2], pd->ppp->ifname)) { + if (temp) { + pd->temp_down_speed = down_speed; + pd->temp_up_speed = up_speed; + } else { + pd->temp_down_speed = 0; + pd->temp_up_speed = 0; + if (!pd->cur_tr) + pd->cur_tr = get_tr_pd(pd, 0); + pd->cur_tr->down_speed = down_speed; + pd->cur_tr->down_burst = down_burst; + pd->cur_tr->up_speed = up_speed; + pd->cur_tr->up_burst = up_burst; + } + triton_context_call(pd->ppp->ctrl->ctx, (triton_event_func)shaper_change, pd); + if (!all) { + found = 1; + break; + } + } + } + pthread_rwlock_unlock(&shaper_lock); + + if (!all && !found) + cli_send(cli, "not found\r\n"); + + return CLI_CMD_OK; +} + +static void shaper_restore_help(char * const *f, int f_cnt, void *cli) +{ + cli_send(cli, "shaper restore <interface> - restores shaper settings on specified interface made by 'shaper change' command with 'temp' flag\r\n"); + cli_send(cli, "shaper restore all - restores shaper settings on all interfaces made by 'shaper change' command with 'temp' flag\r\n"); +} + +static void shaper_restore(struct shaper_pd_t *pd) +{ + remove_shaper(pd->ppp->ifname); + + if (pd->cur_tr) + install_shaper(pd->ppp->ifname, pd->cur_tr->down_speed, pd->cur_tr->down_burst, pd->cur_tr->up_speed, pd->cur_tr->up_burst); +} + +static int shaper_restore_exec(const char *cmd, char * const *f, int f_cnt, void *cli) +{ + struct shaper_pd_t *pd; + int all, found = 0;; + + if (f_cnt != 3) + return CLI_CMD_SYNTAX; + + if (strcmp(f[2], "all")) + all = 0; + else + all = 1; + + pthread_rwlock_rdlock(&shaper_lock); + if (all) { + temp_down_speed = 0; + temp_up_speed = 0; + } + list_for_each_entry(pd, &shaper_list, entry) { + if (!pd->temp_down_speed) + continue; + if (all || !strcmp(f[2], pd->ppp->ifname)) { + pd->temp_down_speed = 0; + pd->temp_up_speed = 0; + triton_context_call(pd->ppp->ctrl->ctx, (triton_event_func)shaper_restore, pd); + if (!all) { + found = 1; + break; + } + } + } + pthread_rwlock_unlock(&shaper_lock); + + if (!all && !found) + cli_send(cli, "not found\r\n"); + + return CLI_CMD_OK; +} + +static void print_rate(const struct ppp_t *ppp, char *buf) +{ + struct shaper_pd_t *pd = find_pd((struct ppp_t *)ppp, 0); + + if (pd && (pd->down_speed || pd->up_speed)) + sprintf(buf, "%i/%i", pd->down_speed, pd->up_speed); + else + *buf = 0; +} + +static void shaper_ctx_close(struct triton_context_t *ctx) +{ + struct time_range_t *r; + + while (!list_empty(&time_range_list)) { + r = list_entry(time_range_list.next, typeof(*r), entry); + list_del(&r->entry); + if (r->begin.tpd) + triton_timer_del(&r->begin); + if (r->end.tpd) + triton_timer_del(&r->end); + _free(r); + } + + triton_context_unregister(ctx); +} + +static void update_shaper_tr(struct shaper_pd_t *pd) +{ + struct time_range_pd_t *tr; + + list_for_each_entry(tr, &pd->tr_list, entry) { + if (tr->id != time_range_id) + continue; + pd->cur_tr = tr; + break; + } + + if (pd->temp_down_speed || pd->temp_up_speed) + return; + + if (pd->down_speed || pd->up_speed) { + if (pd->cur_tr && pd->down_speed == pd->cur_tr->down_speed && pd->up_speed == pd->cur_tr->up_speed) + return; + remove_shaper(pd->ppp->ifname); + } + + if (pd->cur_tr && (pd->cur_tr->down_speed || pd->cur_tr->up_speed)) { + pd->down_speed = pd->cur_tr->down_speed; + pd->up_speed = pd->cur_tr->up_speed; + if (!install_shaper(pd->ppp->ifname, pd->cur_tr->down_speed, pd->cur_tr->down_burst, pd->cur_tr->up_speed, pd->cur_tr->up_burst)) { + if (conf_verbose) + log_ppp_info2("tbf: changed shaper %i/%i (Kbit)\n", pd->cur_tr->down_speed, pd->cur_tr->up_speed); + } + } else + if (conf_verbose) + log_ppp_info2("tbf: removed shaper\n"); + +} + +static void time_range_begin_timer(struct triton_timer_t *t) +{ + struct time_range_t *tr = container_of(t, typeof(*tr), begin); + struct shaper_pd_t *pd; + + time_range_id = tr->id; + + log_debug("tbf: time_range_begin_timer: id=%i\n", time_range_id); + + pthread_rwlock_rdlock(&shaper_lock); + list_for_each_entry(pd, &shaper_list, entry) + triton_context_call(pd->ppp->ctrl->ctx, (triton_event_func)update_shaper_tr, pd); + pthread_rwlock_unlock(&shaper_lock); +} + +static void time_range_end_timer(struct triton_timer_t *t) +{ + struct shaper_pd_t *pd; + + time_range_id = 0; + + log_debug("tbf: time_range_end_timer\n"); + + pthread_rwlock_rdlock(&shaper_lock); + list_for_each_entry(pd, &shaper_list, entry) + triton_context_call(pd->ppp->ctrl->ctx, (triton_event_func)update_shaper_tr, pd); + pthread_rwlock_unlock(&shaper_lock); +} + +static struct time_range_t *parse_range(const char *val) +{ + char *endptr; + int id; + time_t t; + struct tm begin_tm, end_tm; + struct time_range_t *r; + + id = strtol(val, &endptr, 10); + if (*endptr != ',') + return NULL; + if (id <= 0) + return NULL; + + time(&t); + localtime_r(&t, &begin_tm); + begin_tm.tm_sec = 1; + end_tm = begin_tm; + end_tm.tm_sec = 0; + + endptr = strptime(endptr + 1, "%H:%M", &begin_tm); + if (*endptr != '-') + return NULL; + + endptr = strptime(endptr + 1, "%H:%M", &end_tm); + if (*endptr) + return NULL; + + r = _malloc(sizeof(*r)); + memset(r, 0, sizeof(*r)); + + r->id = id; + r->begin.expire_tv.tv_sec = mktime(&begin_tm); + r->begin.period = 24 * 60 * 60 * 1000; + r->begin.expire = time_range_begin_timer; + r->end.expire_tv.tv_sec = mktime(&end_tm); + r->end.period = 24 * 60 * 60 * 1000; + r->end.expire = time_range_end_timer; + + return r; +} + +static void load_time_ranges(void) +{ + struct conf_sect_t *s = conf_get_section("tbf"); + struct conf_option_t *opt; + struct time_range_t *r; + time_t ts; + + if (!s) + return; + + time(&ts); + + while (!list_empty(&time_range_list)) { + r = list_entry(time_range_list.next, typeof(*r), entry); + list_del(&r->entry); + if (r->begin.tpd) + triton_timer_del(&r->begin); + if (r->end.tpd) + triton_timer_del(&r->end); + _free(r); + } + + list_for_each_entry(opt, &s->items, entry) { + if (strcmp(opt->name, "time-range")) + continue; + r = parse_range(opt->val); + if (r) { + list_add_tail(&r->entry, &time_range_list); + if (r->begin.expire_tv.tv_sec > r->end.expire_tv.tv_sec) { + if (ts >= r->begin.expire_tv.tv_sec - 24*60*60 && ts <= r->end.expire_tv.tv_sec) + time_range_begin_timer(&r->begin); + } else { + if (ts >= r->begin.expire_tv.tv_sec && ts <= r->end.expire_tv.tv_sec) + time_range_begin_timer(&r->begin); + } + if (r->begin.expire_tv.tv_sec < ts) + r->begin.expire_tv.tv_sec += 24 * 60 * 60; + if (r->end.expire_tv.tv_sec < ts) + r->end.expire_tv.tv_sec += 24 * 60 * 60; + triton_timer_add(&shaper_ctx, &r->begin, 1); + triton_timer_add(&shaper_ctx, &r->end, 1); + } else + log_emerg("tbf: failed to parse time-range '%s'\n", opt->val); + } +} + +#ifdef RADIUS +static int parse_attr_opt(const char *opt) +{ + struct rad_dict_attr_t *attr; + struct rad_dict_vendor_t *vendor; + + if (conf_vendor) + vendor = rad_dict_find_vendor_id(conf_vendor); + else + vendor = NULL; + + if (conf_vendor) { + if (vendor) + attr = rad_dict_find_vendor_attr(vendor, opt); + else + attr = NULL; + }else + attr = rad_dict_find_attr(opt); + + if (attr) + return attr->id; + + return atoi(opt); +} + +static int parse_vendor_opt(const char *opt) +{ + struct rad_dict_vendor_t *vendor; + + vendor = rad_dict_find_vendor_name(opt); + if (vendor) + return vendor->id; + + return atoi(opt); +} +#endif + +static void load_config(void) +{ + const char *opt; + +#ifdef RADIUS + opt = conf_get_opt("tbf", "vendor"); + if (opt) + conf_vendor = parse_vendor_opt(opt); + + opt = conf_get_opt("tbf", "attr"); + if (opt) { + conf_attr_down = parse_attr_opt(opt); + conf_attr_up = parse_attr_opt(opt); + } + + opt = conf_get_opt("tbf", "attr-down"); + if (opt) + conf_attr_down = parse_attr_opt(opt); + + opt = conf_get_opt("tbf", "attr-up"); + if (opt) + conf_attr_up = parse_attr_opt(opt); + + if (conf_attr_up <= 0 || conf_attr_down <= 0) { + log_emerg("tbf: incorrect attribute(s), tbf disabled...\n"); + return; + } +#endif + + opt = conf_get_opt("tbf", "burst-factor"); + if (opt) { + conf_down_burst_factor = strtod(opt, NULL); + conf_up_burst_factor = conf_down_burst_factor * 10; + } + + opt = conf_get_opt("tbf", "down-burst-factor"); + if (opt) + conf_down_burst_factor = strtod(opt, NULL); + + opt = conf_get_opt("tbf", "up-burst-factor"); + if (opt) + conf_up_burst_factor = strtod(opt, NULL); + + opt = conf_get_opt("tbf", "latency"); + if (opt && atoi(opt) > 0) + conf_latency = atoi(opt); + + opt = conf_get_opt("tbf", "mpu"); + if (opt && atoi(opt) >= 0) + conf_mpu = atoi(opt); + + opt = conf_get_opt("tbf", "verbose"); + if (opt && atoi(opt) > 0) + conf_verbose = 1; + + triton_context_call(&shaper_ctx, (triton_event_func)load_time_ranges, NULL); +} + +static int clock_init(void) +{ + FILE *fp; + uint32_t clock_res; + uint32_t t2us; + uint32_t us2t; + + fp = fopen("/proc/net/psched", "r"); + + if (!fp) { + log_emerg("tbf: failed to open /proc/net/psched: %s\n", strerror(errno)); + return -1; + } + + if (fscanf(fp, "%08x%08x%08x", &t2us, &us2t, &clock_res) != 3) { + log_emerg("tbf: failed to parse /proc/net/psched\n"); + fclose(fp); + return -1; + } + + fclose(fp); + + /* compatibility hack: for old iproute binaries (ignoring + * the kernel clock resolution) the kernel advertises a + * tick multiplier of 1000 in case of nano-second resolution, + * which really is 1. */ + if (clock_res == 1000000000) + t2us = us2t; + + clock_factor = (double)clock_res / TIME_UNITS_PER_SEC; + tick_in_usec = (double)t2us / us2t * clock_factor; + + return 0; +} + +static void __init init(void) +{ + if (clock_init()) + return; + + triton_context_register(&shaper_ctx, NULL); + triton_context_wakeup(&shaper_ctx); + + load_config(); + +#ifdef RADIUS + triton_event_register_handler(EV_RADIUS_ACCESS_ACCEPT, (triton_event_func)ev_radius_access_accept); + triton_event_register_handler(EV_RADIUS_COA, (triton_event_func)ev_radius_coa); +#endif + triton_event_register_handler(EV_CTRL_FINISHED, (triton_event_func)ev_ctrl_finished); + triton_event_register_handler(EV_SHAPER, (triton_event_func)ev_shaper); + triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); + + cli_register_simple_cmd2(shaper_change_exec, shaper_change_help, 2, "shaper", "change"); + cli_register_simple_cmd2(shaper_restore_exec, shaper_restore_help, 2, "shaper", "restore"); + cli_show_ses_register("rate-limit", "rate limit down-stream/up-stream (Kbit)", print_rate); +} + diff --git a/accel-pppd/extra/sigchld.c b/accel-pppd/extra/sigchld.c new file mode 100644 index 00000000..abfd42f1 --- /dev/null +++ b/accel-pppd/extra/sigchld.c @@ -0,0 +1,116 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <sys/wait.h> + +#include "triton.h" +#include "spinlock.h" +#include "log.h" + +#include "sigchld.h" + +#include "memdebug.h" + +static LIST_HEAD(handlers); +static int lock_refs; +static pthread_mutex_t handlers_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t refs_cond = PTHREAD_COND_INITIALIZER; +static pthread_t sigchld_thr; + +static void* sigchld_thread(void *arg) +{ + sigset_t set; + struct sigchld_handler_t *h, *h0; + pid_t pid; + int status, sig; + + sigfillset(&set); + sigdelset(&set, SIGKILL); + sigdelset(&set, SIGSTOP); + pthread_sigmask(SIG_BLOCK, &set, NULL); + + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigaddset(&set, SIGQUIT); + + while (1) { + pid = waitpid(0, &status, 0); + if (pid < 0) { + if (errno == EINTR) + continue; + if (errno == ECHILD) { + sigwait(&set, &sig); + if (sig == SIGQUIT) + break; + continue; + } + log_error("sigchld: waitpid: %s\n", strerror(errno)); + continue; + } + + pthread_mutex_lock(&handlers_lock); + while (lock_refs) + pthread_cond_wait(&refs_cond, &handlers_lock); + + h0 = NULL; + list_for_each_entry(h, &handlers, entry) { + if (h->pid == pid) { + h0 = h; + list_del(&h0->entry); + pthread_mutex_lock(&h0->lock); + break; + } + } + pthread_mutex_unlock(&handlers_lock); + if (h0) { + h0->handler(h0, WEXITSTATUS(status)); + h0->pid = 0; + pthread_mutex_unlock(&h0->lock); + } + } + + return NULL; +} + +void __export sigchld_register_handler(struct sigchld_handler_t *h) +{ + pthread_mutex_init(&h->lock, NULL); + + pthread_mutex_lock(&handlers_lock); + list_add_tail(&h->entry, &handlers); + pthread_mutex_unlock(&handlers_lock); +} + +void __export sigchld_unregister_handler(struct sigchld_handler_t *h) +{ + pthread_mutex_lock(&handlers_lock); + pthread_mutex_lock(&h->lock); + if (h->pid) { + list_del(&h->entry); + h->pid = 0; + } + pthread_mutex_unlock(&h->lock); + pthread_mutex_unlock(&handlers_lock); +} + +void __export sigchld_lock() +{ + pthread_mutex_lock(&handlers_lock); + ++lock_refs; + pthread_mutex_unlock(&handlers_lock); +} + +void __export sigchld_unlock() +{ + pthread_mutex_lock(&handlers_lock); + if (--lock_refs == 0) + pthread_cond_signal(&refs_cond); + pthread_mutex_unlock(&handlers_lock); +} + +static void __init init(void) +{ + if (pthread_create(&sigchld_thr, NULL, sigchld_thread, NULL)) + log_emerg("sigchld: pthread_create: %s\n", strerror(errno)); +} diff --git a/accel-pppd/extra/sigchld.h b/accel-pppd/extra/sigchld.h new file mode 100644 index 00000000..27d1adf4 --- /dev/null +++ b/accel-pppd/extra/sigchld.h @@ -0,0 +1,21 @@ +#ifndef __SIGCHLD_H +#define __SIGCHLD_H + +#include <pthread.h> +#include "list.h" + +struct sigchld_handler_t +{ + struct list_head entry; + pthread_mutex_t lock; + pid_t pid; + void (*handler)(struct sigchld_handler_t *, int status); +}; + +void sigchld_register_handler(struct sigchld_handler_t *); +void sigchld_unregister_handler(struct sigchld_handler_t *); +void sigchld_lock(); +void sigchld_unlock(); + +#endif + |