diff options
-rw-r--r-- | accel-pptpd/accel-pptp.conf | 6 | ||||
-rw-r--r-- | accel-pptpd/extra/CMakeLists.txt | 2 | ||||
-rw-r--r-- | accel-pptpd/extra/shaper_tbf.c | 571 |
3 files changed, 579 insertions, 0 deletions
diff --git a/accel-pptpd/accel-pptp.conf b/accel-pptpd/accel-pptp.conf index 62a7c88..163bdcb 100644 --- a/accel-pptpd/accel-pptp.conf +++ b/accel-pptpd/accel-pptp.conf @@ -13,6 +13,7 @@ radius ippool sigchld pppd_compat +#shaper_tbf [core] log-error=/var/log/accel-pptp/core.log @@ -89,3 +90,8 @@ ip-down=/etc/ppp/ip-down ip-change=/etc/ppp/ip-change radattr-prefix=/var/run/radattr verbose=1 + +[tbf] +#attr=Filter-Id +#burst_factor=0.1 +#latency=50 diff --git a/accel-pptpd/extra/CMakeLists.txt b/accel-pptpd/extra/CMakeLists.txt index a14a5a3..600c861 100644 --- a/accel-pptpd/extra/CMakeLists.txt +++ b/accel-pptpd/extra/CMakeLists.txt @@ -1,6 +1,8 @@ ADD_LIBRARY(pppd_compat SHARED pppd_compat.c) ADD_LIBRARY(ippool SHARED ippool.c) ADD_LIBRARY(sigchld SHARED sigchld.c) +ADD_LIBRARY(shaper_tbf SHARED shaper_tbf.c) +TARGET_LINK_LIBRARIES(shaper_tbf nl) INSTALL(TARGETS pppd_compat ippool sigchld LIBRARY DESTINATION usr/lib/accel-pptp diff --git a/accel-pptpd/extra/shaper_tbf.c b/accel-pptpd/extra/shaper_tbf.c new file mode 100644 index 0000000..07168d5 --- /dev/null +++ b/accel-pptpd/extra/shaper_tbf.c @@ -0,0 +1,571 @@ +#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 <netlink/netlink.h> +#include <netlink/route/qdisc.h> +#include <netlink/route/sch/tbf.h> +#include <netlink/route/class.h> +#include <netlink/route/cls/u32.h> +#include <netlink/route/cls/police.h> + +#include "triton.h" +#include "events.h" +#include "radius.h" +#include "log.h" +#include "ppp.h" + +#define TIME_UNITS_PER_SEC 1000000 + +//static int conf_verbose = 0; +static int conf_attr_down = 11; //Filter-Id +static int conf_attr_up = 11; //Filter-Id +static double conf_burst_factor = 0.1; +static int conf_latency = 50; +static int conf_mpu = 0; + +static double tick_in_usec = 1; +static double clock_factor = 1; + +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_handle *h, int ifindex, int speed) +{ + 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 = rate*conf_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_handle *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_handle *h, int ifindex, int speed) +{ + double rate = speed*1000/8; + double bucket = rate*conf_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 void install_shaper(const char *ifname, int down_speed, int up_speed) +{ + struct nl_handle *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; + } + + h = nl_handle_alloc(); + if (!h) { + log_ppp_error("tbf: nl_handle_alloc failed\n"); + return; + } + + err = nl_connect(h, NETLINK_ROUTE); + if (err < 0) { + log_ppp_error("tbf: nl_connect: %s", strerror(errno)); + goto out; + } + + if (down_speed) + install_tbf(h, ifr.ifr_ifindex, down_speed); + + if (up_speed) { + install_ingress(h, ifr.ifr_ifindex); + install_filter(h, ifr.ifr_ifindex, up_speed); + } + + nl_close(h); +out: + nl_handle_destroy(h); +} + +static void remove_shaper(const char *ifname) +{ + struct nl_handle *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; + } + + 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_handle_alloc(); + if (!h) { + log_ppp_error("tbf: nl_handle_alloc failed\n"); + return; + } + + err = nl_connect(h, NETLINK_ROUTE); + if (err < 0) { + log_ppp_error("tbf: nl_connect: %s", strerror(errno)); + goto out; + } + + 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); +out: + nl_handle_destroy(h); + return; + +out_err: + log_ppp_error("tbf: failed to remove shaper\n"); + + nlmsg_free(pmsg); + nl_close(h); + nl_handle_destroy(h); +} + + +static int parse_attr(struct rad_attr_t *attr) +{ + if (attr->attr->type == ATTR_TYPE_STRING) + return atoi(attr->val.string); + else if (attr->attr->type == ATTR_TYPE_INTEGER) + return attr->val.integer; + + return 0; +} + +static void ev_radius_access_accept(struct ev_radius_t *ev) +{ + struct rad_attr_t *attr; + int down_speed = 0; + int up_speed = 0; + + list_for_each_entry(attr, &ev->reply->attrs, entry) { + if (attr->attr->id == conf_attr_down) + down_speed = parse_attr(attr); + if (attr->attr->id == conf_attr_up) + up_speed = parse_attr(attr); + } + + if (down_speed > 0 && up_speed > 0) + install_shaper(ev->ppp->ifname, down_speed, up_speed); +} + +static void ev_radius_coa(struct ev_radius_t *ev) +{ + struct rad_attr_t *attr; + int down_speed = 0; + int up_speed = 0; + + list_for_each_entry(attr, &ev->request->attrs, entry) { + if (attr->attr->id == conf_attr_down) + down_speed = parse_attr(attr); + if (attr->attr->id == conf_attr_up) + up_speed = parse_attr(attr); + } + + remove_shaper(ev->ppp->ifname); + + if (down_speed > 0 && up_speed > 0) + install_shaper(ev->ppp->ifname, down_speed, up_speed); +} + +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 int parse_attr_opt(const char *opt) +{ + struct rad_dict_attr_t *attr; + + attr = rad_dict_find_attr(opt); + if (attr) + return attr->id; + + return atoi(opt); +} + +static void __init init(void) +{ + const char *opt; + + if (clock_init()) + return; + + 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); + + opt = conf_get_opt("tbf", "burst-factor"); + if (opt) + conf_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); + + if (conf_attr_up <= 0 || conf_attr_down <= 0) { + log_emerg("tbf: incorrect attribute(s), tbf disabled...\n"); + return; + } + + 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); +} + |