summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKozlov Dmitry <xeb@mail.ru>2012-01-22 21:58:33 +0400
committerKozlov Dmitry <xeb@mail.ru>2012-01-22 22:09:10 +0400
commitbdf5bd00fa21707a7f09c95e76106682355f0927 (patch)
tree0f72ae54b6fa21d3f546abde7573d85b4044f025
parentf7b2ca85f9cda5d542aa0fe638926682ae86ddf4 (diff)
downloadaccel-ppp-bdf5bd00fa21707a7f09c95e76106682355f0927.tar.gz
accel-ppp-bdf5bd00fa21707a7f09c95e76106682355f0927.zip
implemented advanced shaper
-rw-r--r--accel-pppd/CMakeLists.txt1
-rw-r--r--accel-pppd/accel-ppp.conf19
-rw-r--r--accel-pppd/accel-ppp.conf.538
-rw-r--r--accel-pppd/extra/chap-secrets.c2
-rw-r--r--accel-pppd/shaper/CMakeLists.txt2
-rw-r--r--accel-pppd/shaper/libnetlink.c692
-rw-r--r--accel-pppd/shaper/libnetlink.h115
-rw-r--r--accel-pppd/shaper/limiter.c536
-rw-r--r--accel-pppd/shaper/shaper.c929
-rw-r--r--accel-pppd/shaper/shaper.h23
-rw-r--r--accel-pppd/shaper/tc_core.c211
-rw-r--r--accel-pppd/shaper/tc_core.h33
12 files changed, 2598 insertions, 3 deletions
diff --git a/accel-pppd/CMakeLists.txt b/accel-pppd/CMakeLists.txt
index bee823d..4e6c6d6 100644
--- a/accel-pppd/CMakeLists.txt
+++ b/accel-pppd/CMakeLists.txt
@@ -39,6 +39,7 @@ ADD_SUBDIRECTORY(auth)
ADD_SUBDIRECTORY(logs)
ADD_SUBDIRECTORY(extra)
ADD_SUBDIRECTORY(ipv6)
+ADD_SUBDIRECTORY(shaper)
ADD_EXECUTABLE(accel-pppd
ppp/ppp.c
diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf
index b65f41b..c59b205 100644
--- a/accel-pppd/accel-ppp.conf
+++ b/accel-pppd/accel-ppp.conf
@@ -19,7 +19,8 @@ ippool
sigchld
pppd_compat
-#shaper_tbf
+#shaper
+#shaper_tbf (obsolete)
#chap-secrets
#net-snmp
#logwtmp
@@ -142,7 +143,21 @@ verbose=1
gw-ip-address=192.168.100.1
#chap-secrets=/etc/ppp/chap-secrets
-[tbf]
+[shaper]
+#attr=Filter-Id
+#down-burst-factor=0.1
+#up-burst-factor=1.0
+#latency=50
+#mpu=0
+#r2q=10
+#quantum=1500
+#ifb=ifb0
+up-limiter=police
+down-limiter=tbf
+verbose=1
+
+#tbf is obsolete, use shaper module
+#[tbf]
#attr=Filter-Id
#down-burst-factor=0.1
#up-burst-factor=1.0
diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5
index f294486..c622ce3 100644
--- a/accel-pppd/accel-ppp.conf.5
+++ b/accel-pppd/accel-ppp.conf.5
@@ -523,3 +523,41 @@ Specifies acceptable rate of connections, for example limit=1/s or limit=10/m.
.TP
.BI "timeout=" n
Specifies timeout in seconds after which module doesn't check rate until burst number of connections will be arrived.
+.TP
+.SH [shaper]
+.br
+This module controls shaper.
+.TP
+.BI "attr=" name
+Specifies which radius attribute contains rate information. Default - Filter-ID.
+.TP
+.BI "attr-up=" name
+.TP
+.BI "attr-down=" name
+Specifies which radius attributes contains rate information for upstream and downstream respectively.
+.TP
+.BI "burst-factor=" n
+Burst will be calculated as rate multyply burst-factor.
+.TP
+.BI "up-burst-factor=" n
+.TP
+.BI "down-burst-factor=" n
+Specifies burst factor for upstream and downstream respectively.
+.TP
+.BI "latency=" n
+Specifies latency (in miliseconds) parameter of tbf qdisc.
+.TP
+.BI "mpu=" n
+Specifies mpu parameter of tbf qdisc and policer.
+.TP
+.BI "r2q=" n
+Specifies r2q parameter of root htb qdisc.
+.TP
+.BI "quantum=" n
+Specifies quantum parameter of htb classes.
+.TP
+.BI "up-limiter=" police|htb
+Specifes upstream rate limiting method.
+.TP
+.BI "down-limiter=" tbf|htb
+Specifies downstream rate limiting method.
diff --git a/accel-pppd/extra/chap-secrets.c b/accel-pppd/extra/chap-secrets.c
index e8f1b58..db1bbb2 100644
--- a/accel-pppd/extra/chap-secrets.c
+++ b/accel-pppd/extra/chap-secrets.c
@@ -280,4 +280,4 @@ static void init(void)
triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
}
-DEFINE_INIT(200, init);
+DEFINE_INIT(51, init);
diff --git a/accel-pppd/shaper/CMakeLists.txt b/accel-pppd/shaper/CMakeLists.txt
new file mode 100644
index 0000000..989aceb
--- /dev/null
+++ b/accel-pppd/shaper/CMakeLists.txt
@@ -0,0 +1,2 @@
+ADD_LIBRARY(shaper SHARED shaper.c limiter.c tc_core.c libnetlink.c)
+
diff --git a/accel-pppd/shaper/libnetlink.c b/accel-pppd/shaper/libnetlink.c
new file mode 100644
index 0000000..74cd5cb
--- /dev/null
+++ b/accel-pppd/shaper/libnetlink.c
@@ -0,0 +1,692 @@
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+#include "log.h"
+
+int rcvbuf = 1024 * 1024;
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ if (rth->fd >= 0) {
+ close(rth->fd);
+ rth->fd = -1;
+ }
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+ int protocol)
+{
+ socklen_t addr_len;
+ int sndbuf = 32768;
+
+ memset(rth, 0, sizeof(*rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+ if (rth->fd < 0) {
+ log_error("libnetlink: ""Cannot open netlink socket: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+ log_error("libnetlink: ""SO_SNDBUF: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
+ log_error("libnetlink: ""SO_RCVBUF: %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ log_error("libnetlink: ""Cannot bind netlink socket: %s\n", strerror(errno));
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ log_error("libnetlink: ""Cannot getsockname: %s\n", strerror(errno));
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ log_error("libnetlink: ""Wrong address length %d\n", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ log_error("libnetlink: ""Wrong address family %d\n", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return send(rth->fd, (void*)&req, sizeof(req), 0);
+}
+
+int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
+{
+ return send(rth->fd, buf, len, 0);
+}
+
+int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int len)
+{
+ struct nlmsghdr *h;
+ int status;
+ char resp[1024];
+
+ status = send(rth->fd, buf, len, 0);
+ if (status < 0)
+ return status;
+
+ /* Check for immediate errors */
+ status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK);
+ if (status < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ return -1;
+ }
+
+ for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, status);
+ h = NLMSG_NEXT(h, status)) {
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+ log_error("libnetlink: ""ERROR truncated\n");
+ else
+ errno = -err->error;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr;
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof(nlh) },
+ { .iov_base = req, .iov_len = len }
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg)
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ iov.iov_base = buf;
+ while (1) {
+ int status;
+ const struct rtnl_dump_filter_arg *a;
+ int found_done = 0;
+ int msglen = 0;
+
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ log_error("libnetlink: ""netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ if (status == 0) {
+ log_error("libnetlink: ""EOF on netlink\n");
+ return -1;
+ }
+
+ for (a = arg; a->filter; a++) {
+ struct nlmsghdr *h = (struct nlmsghdr*)buf;
+ msglen = status;
+
+ while (NLMSG_OK(h, msglen)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+ if (a->junk) {
+ err = a->junk(&nladdr, h,
+ a->arg2);
+ if (err < 0)
+ return err;
+ }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE) {
+ found_done = 1;
+ break; /* process next filter */
+ }
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr,
+ "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ log_error("libnetlink: ""RTNETLINK answers: %s\n", strerror(errno));
+ }
+ return -1;
+ }
+ err = a->filter(&nladdr, h, a->arg1);
+ if (err < 0)
+ return err;
+
+skip_it:
+ h = NLMSG_NEXT(h, msglen);
+ }
+ }
+
+ if (found_done)
+ return 0;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ log_error("libnetlink: ""Message truncated\n");
+ continue;
+ }
+ if (msglen) {
+ log_error("libnetlink: ""!!!Remnant of size %d\n", msglen);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2)
+{
+ const struct rtnl_dump_filter_arg a[2] = {
+ { .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 },
+ { .filter = NULL, .arg1 = NULL, .junk = NULL, .arg2 = NULL }
+ };
+
+ return rtnl_dump_filter_l(rth, a);
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg, int ignore_einval)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = (void*) n,
+ .iov_len = n->nlmsg_len
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+
+ if (answer == NULL)
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ log_error("libnetlink: ""Cannot talk to rtnetlink: %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(buf,0,sizeof(buf));
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ log_error("libnetlink: ""netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+ if (status == 0) {
+ log_error("libnetlink: ""EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ log_error("libnetlink: ""sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ log_error("libnetlink: ""Truncated message\n");
+ return -1;
+ }
+ log_error("libnetlink: ""!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ if (nladdr.nl_pid != peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ if (junk) {
+ err = junk(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+ /* Don't forget to skip that message. */
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (l < sizeof(struct nlmsgerr)) {
+ log_error("libnetlink: ""ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ if (errno == 0 || (errno == EINVAL && ignore_einval)) {
+ if (answer)
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+ log_error("libnetlink: ""RTNETLINK answers: %s\n", strerror(errno));
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ log_error("libnetlink: ""Unexpected reply!!!\n");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ log_error("libnetlink: ""Message truncated\n");
+ continue;
+ }
+ if (status) {
+ log_error("libnetlink: ""!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+ rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[8192];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ log_error("libnetlink: ""netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ if (errno == ENOBUFS)
+ continue;
+ return -1;
+ }
+ if (status == 0) {
+ log_error("libnetlink: ""EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ log_error("libnetlink: ""Sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ log_error("libnetlink: ""Truncated message\n");
+ return -1;
+ }
+ log_error("libnetlink: ""!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ log_error("libnetlink: ""Message truncated\n");
+ continue;
+ }
+ if (status) {
+ log_error("libnetlink: ""!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct sockaddr_nl nladdr;
+ char buf[8192];
+ struct nlmsghdr *h = (void*)buf;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ while (1) {
+ int err, len, type;
+ int l;
+
+ status = fread(&buf, 1, sizeof(*h), rtnl);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ log_error("libnetlink: ""rtnl_from_file: fread");
+ return -1;
+ }
+ if (status == 0)
+ return 0;
+
+ len = h->nlmsg_len;
+ type= h->nlmsg_type;
+ l = len - sizeof(*h);
+
+ if (l<0 || len>sizeof(buf)) {
+ log_error("libnetlink: ""!!!malformed message: len=%d @%lu\n",
+ len, ftell(rtnl));
+ return -1;
+ }
+
+ status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+ if (status < 0) {
+ log_error("libnetlink: ""rtnl_from_file: fread");
+ return -1;
+ }
+ if (status < l) {
+ log_error("libnetlink: ""rtnl-from_file: truncated message\n");
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+ if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+ fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+ log_error("libnetlink: ""addattr_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+ if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) {
+ log_error("libnetlink: ""addraw_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+
+ memcpy(NLMSG_TAIL(n), data, len);
+ memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+ return 0;
+}
+
+struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type)
+{
+ struct rtattr *nest = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, NULL, 0);
+ return nest;
+}
+
+int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
+{
+ nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest;
+ return n->nlmsg_len;
+}
+
+struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type,
+ const void *data, int len)
+{
+ struct rtattr *start = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, data, len);
+ addattr_nest(n, maxlen, type);
+ return start;
+}
+
+int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start)
+{
+ struct rtattr *nest = (void *)start + NLMSG_ALIGN(start->rta_len);
+
+ start->rta_len = (void *)NLMSG_TAIL(n) - (void *)start;
+ addattr_nest_end(n, nest);
+ return n->nlmsg_len;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+ const void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ log_error("libnetlink: ""!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return 0;
+}
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ int i = 0;
+
+ memset(tb, 0, sizeof(struct rtattr *) * max);
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max && i < max)
+ tb[i++] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ log_error("libnetlink: ""!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return i;
+}
+
+int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len)
+{
+ if (RTA_PAYLOAD(rta) < len)
+ return -1;
+ if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) {
+ rta = RTA_DATA(rta) + RTA_ALIGN(len);
+ return parse_rtattr_nested(tb, max, rta);
+ }
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ return 0;
+}
diff --git a/accel-pppd/shaper/libnetlink.h b/accel-pppd/shaper/libnetlink.h
new file mode 100644
index 0000000..63ba5ad
--- /dev/null
+++ b/accel-pppd/shaper/libnetlink.h
@@ -0,0 +1,115 @@
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_link.h>
+#include <linux/if_addr.h>
+#include <linux/neighbour.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+extern int rcvbuf;
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+ struct nlmsghdr *n, void *);
+
+struct rtnl_dump_filter_arg
+{
+ rtnl_filter_t filter;
+ void *arg1;
+ rtnl_filter_t junk;
+ void *arg2;
+};
+
+extern int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg);
+extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2);
+
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg, int ignore_einval);
+extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int);
+extern int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int);
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
+extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
+extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type);
+extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
+extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len);
+extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
+({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
+ __parse_rtattr_nested_compat(tb, max, rta, len); })
+
+extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
+ void *jarg);
+extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
+ void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+#ifndef IFA_PAYLOAD
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
+
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+#ifndef IFLA_PAYLOAD
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
+
+#endif /* __LIBNETLINK_H__ */
+
diff --git a/accel-pppd/shaper/limiter.c b/accel-pppd/shaper/limiter.c
new file mode 100644
index 0000000..d985682
--- /dev/null
+++ b/accel-pppd/shaper/limiter.c
@@ -0,0 +1,536 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/pkt_cls.h>
+#include <linux/pkt_sched.h>
+#include <linux/tc_act/tc_mirred.h>
+#include <linux/tc_act/tc_skbedit.h>
+
+#include "log.h"
+#include "ppp.h"
+
+#include "shaper.h"
+#include "tc_core.h"
+#include "libnetlink.h"
+
+#define TCA_BUF_MAX 64*1024
+#define MAX_MSG 16384
+
+static __thread struct {
+ struct nlmsghdr n;
+ struct tcmsg t;
+ char buf[TCA_BUF_MAX];
+} req;
+
+struct qdisc_opt
+{
+ char *kind;
+ int handle;
+ int parent;
+ double latency;
+ int rate;
+ int buffer;
+ int quantum;
+ int defcls;
+ int (*qdisc)(struct qdisc_opt *opt, struct nlmsghdr *n);
+};
+
+static int qdisc_tbf(struct qdisc_opt *qopt, struct nlmsghdr *n)
+{
+ struct tc_tbf_qopt opt;
+ __u32 rtab[256];
+ int mtu = 0;
+ int Rcell_log = -1;
+ unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+ struct rtattr *tail;
+
+ memset(&opt, 0, sizeof(opt));
+
+ opt.rate.rate = qopt->rate;
+ opt.limit = (double)qopt->rate * qopt->latency + qopt->buffer;
+ opt.rate.mpu = conf_mpu;
+ if (tc_calc_rtable(&opt.rate, rtab, Rcell_log, mtu, linklayer) < 0) {
+ log_ppp_error("shaper: failed to calculate rate table.\n");
+ return -1;
+ }
+ opt.buffer = tc_calc_xmittime(opt.rate.rate, qopt->buffer);
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt));
+ addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+
+ return 0;
+}
+
+static int qdisc_htb_root(struct qdisc_opt *qopt, struct nlmsghdr *n)
+{
+ struct tc_htb_glob opt;
+ struct rtattr *tail;
+
+ memset(&opt,0,sizeof(opt));
+
+ opt.rate2quantum = qopt->quantum;
+ opt.version = 3;
+ opt.defcls = qopt->defcls;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 2024, TCA_HTB_INIT, &opt, NLMSG_ALIGN(sizeof(opt)));
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int qdisc_htb_class(struct qdisc_opt *qopt, struct nlmsghdr *n)
+{
+ struct tc_htb_opt opt;
+ __u32 rtab[256],ctab[256];
+ int cell_log=-1,ccell_log = -1;
+ unsigned mtu = 1600;
+ unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+ struct rtattr *tail;
+
+ memset(&opt, 0, sizeof(opt));
+
+ opt.rate.rate = qopt->rate;
+ opt.rate.mpu = conf_mpu;
+ opt.ceil.rate = qopt->rate;
+ opt.ceil.mpu = conf_mpu;
+
+ if (tc_calc_rtable(&opt.rate, rtab, cell_log, mtu, linklayer) < 0) {
+ log_ppp_error("shaper: failed to calculate rate table.\n");
+ return -1;
+ }
+ opt.buffer = tc_calc_xmittime(opt.rate.rate, qopt->buffer);
+
+ if (tc_calc_rtable(&opt.ceil, ctab, ccell_log, mtu, linklayer) < 0) {
+ log_ppp_error("shaper: failed to calculate ceil rate table.\n");
+ return -1;
+ }
+ opt.cbuffer = tc_calc_xmittime(opt.ceil.rate, qopt->buffer);
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
+ addattr_l(n, 2024, TCA_HTB_PARMS, &opt, sizeof(opt));
+ addattr_l(n, 3024, TCA_HTB_RTAB, rtab, 1024);
+ addattr_l(n, 4024, TCA_HTB_CTAB, ctab, 1024);
+ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+ return 0;
+}
+
+static int tc_qdisc_modify(struct rtnl_handle *rth, int ifindex, int cmd, unsigned flags, struct qdisc_opt *opt)
+{
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+ req.n.nlmsg_type = cmd;
+ req.t.tcm_family = AF_UNSPEC;
+
+ req.t.tcm_ifindex = ifindex;
+
+ if (opt->handle)
+ req.t.tcm_handle = opt->handle;
+
+ req.t.tcm_parent = opt->parent;
+
+ if (opt->kind)
+ addattr_l(&req.n, sizeof(req), TCA_KIND, opt->kind, strlen(opt->kind) + 1);
+
+ if (opt->qdisc)
+ opt->qdisc(opt, &req.n);
+
+ if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, cmd == RTM_DELQDISC) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int install_tbf(struct rtnl_handle *rth, int ifindex, int rate, int burst)
+{
+ struct qdisc_opt opt = {
+ .kind = "tbf",
+ .handle = 0x00010000,
+ .parent = TC_H_ROOT,
+ .rate = rate,
+ .buffer = burst,
+ .latency = conf_latency,
+ .qdisc = qdisc_tbf,
+ };
+
+ return tc_qdisc_modify(rth, ifindex, RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, &opt);
+}
+
+static int install_htb(struct rtnl_handle *rth, int ifindex, int rate, int burst)
+{
+ struct qdisc_opt opt1 = {
+ .kind = "htb",
+ .handle = 0x00010000,
+ .parent = TC_H_ROOT,
+ .quantum = conf_r2q,
+ .defcls = 1,
+ .qdisc = qdisc_htb_root,
+ };
+
+ struct qdisc_opt opt2 = {
+ .kind = "htb",
+ .handle = 0x00010001,
+ .parent = 0x00010000,
+ .rate = rate,
+ .buffer = burst,
+ .quantum = conf_quantum,
+ .qdisc = qdisc_htb_class,
+ };
+
+
+ if (tc_qdisc_modify(rth, ifindex, RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, &opt1))
+ return -1;
+
+ if (tc_qdisc_modify(rth, ifindex, RTM_NEWTCLASS, NLM_F_EXCL|NLM_F_CREATE, &opt2))
+ return -1;
+
+ return 0;
+}
+
+static int install_police(struct rtnl_handle *rth, int ifindex, int rate, int burst)
+{
+ __u32 rtab[256];
+ struct rtattr *tail, *tail1, *tail2, *tail3;
+ int Rcell_log = -1;
+ int mtu = 0, flowid = 1;
+ unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
+
+ struct qdisc_opt opt1 = {
+ .kind = "ingress",
+ .handle = 0xffff0000,
+ .parent = TC_H_INGRESS,
+ };
+
+ struct sel {
+ 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 = (double)rate * conf_latency + burst,
+ .burst = tc_calc_xmittime(rate, burst),
+ };
+
+ if (tc_qdisc_modify(rth, ifindex, RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, &opt1))
+ return -1;
+
+ if (tc_calc_rtable(&police.rate, rtab, Rcell_log, mtu, linklayer) < 0) {
+ log_ppp_error("shaper: failed to calculate ceil rate table.\n");
+ return -1;
+ }
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_EXCL|NLM_F_CREATE;
+ req.n.nlmsg_type = RTM_NEWTFILTER;
+ req.t.tcm_family = AF_UNSPEC;
+ req.t.tcm_ifindex = ifindex;
+ req.t.tcm_handle = 1;
+ req.t.tcm_parent = 0xffff0000;
+ req.t.tcm_info = TC_H_MAKE(1 << 16, ntohs(ETH_P_IP));
+
+ addattr_l(&req.n, sizeof(req), TCA_KIND, "u32", 4);
+
+ tail = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, TCA_OPTIONS, NULL, 0);
+
+ tail1 = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, TCA_U32_ACT, NULL, 0);
+
+ tail2 = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, 1, NULL, 0);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, "police", 7);
+
+ tail3 = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_OPTIONS, NULL, 0);
+ addattr_l(&req.n, MAX_MSG, TCA_POLICE_TBF, &police, sizeof(police));
+ addattr_l(&req.n, MAX_MSG, TCA_POLICE_RATE, rtab, 1024);
+ tail3->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail3;
+
+ tail2->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail2;
+
+ tail1->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail1;
+
+ addattr_l(&req.n, MAX_MSG, TCA_U32_CLASSID, &flowid, 4);
+ addattr_l(&req.n, MAX_MSG, TCA_U32_SEL, &sel, sizeof(sel));
+ tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail;
+
+ if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, 0) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int install_htb_ifb(struct rtnl_handle *rth, int ifindex, __u32 priority, int rate, int burst)
+{
+ struct rtattr *tail, *tail1, *tail2, *tail3;
+
+ struct qdisc_opt opt1 = {
+ .kind = "htb",
+ .handle = 0x00010001 + priority,
+ .parent = 0x00010000,
+ .rate = rate,
+ .buffer = burst,
+ .quantum = conf_quantum,
+ .qdisc = qdisc_htb_class,
+ };
+
+ struct qdisc_opt opt2 = {
+ .kind = "ingress",
+ .handle = 0xffff0000,
+ .parent = TC_H_INGRESS,
+ };
+
+ struct sel {
+ struct tc_u32_sel sel;
+ struct tc_u32_key key;
+ } sel = {
+ .sel.nkeys = 1,
+ .sel.flags = TC_U32_TERMINAL,
+ .key.off = 0,
+ };
+
+ struct tc_skbedit p1 = {
+ .action = TC_ACT_PIPE,
+ };
+
+ struct tc_mirred p2 = {
+ .eaction = TCA_EGRESS_REDIR,
+ .action = TC_ACT_STOLEN,
+ .ifindex = conf_ifb_ifindex,
+ };
+
+ if (tc_qdisc_modify(rth, conf_ifb_ifindex, RTM_NEWTCLASS, NLM_F_EXCL|NLM_F_CREATE, &opt1))
+ return -1;
+
+ if (tc_qdisc_modify(rth, ifindex, RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, &opt2))
+ return -1;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_EXCL|NLM_F_CREATE;
+ req.n.nlmsg_type = RTM_NEWTFILTER;
+ req.t.tcm_family = AF_UNSPEC;
+ req.t.tcm_ifindex = ifindex;
+ req.t.tcm_handle = 1;
+ req.t.tcm_parent = 0xffff0000;
+ req.t.tcm_info = TC_H_MAKE(1 << 16, ntohs(ETH_P_IP));
+
+ addattr_l(&req.n, sizeof(req), TCA_KIND, "u32", 4);
+
+ tail = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, TCA_OPTIONS, NULL, 0);
+
+ tail1 = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, TCA_U32_ACT, NULL, 0);
+
+ // action skbedit priority X pipe
+ tail2 = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, 1, NULL, 0);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, "skbedit", 8);
+
+ tail3 = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_OPTIONS, NULL, 0);
+ addattr_l(&req.n, MAX_MSG, TCA_SKBEDIT_PARMS, &p1, sizeof(p1));
+ addattr_l(&req.n, MAX_MSG, TCA_SKBEDIT_PRIORITY, &priority, sizeof(priority));
+ tail3->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail3;
+
+ tail2->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail2;
+
+ tail1->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail1;
+
+ // action mirred egress redirect dev ifb0
+ tail2 = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, 2, NULL, 0);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, "mirred", 7);
+
+ tail3 = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, MAX_MSG, TCA_ACT_OPTIONS, NULL, 0);
+ addattr_l(&req.n, MAX_MSG, TCA_MIRRED_PARMS, &p2, sizeof(p2));
+ tail3->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail3;
+
+ tail2->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail2;
+
+ tail1->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail1;
+ //
+
+ addattr32(&req.n, 4096, TCA_U32_CLASSID, 1);
+ addattr_l(&req.n, MAX_MSG, TCA_U32_SEL, &sel, sizeof(sel));
+ tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail;
+
+ if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, 0) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int remove_root(struct rtnl_handle *rth, int ifindex)
+{
+ struct qdisc_opt opt = {
+ .handle = 0x00010000,
+ .parent = TC_H_ROOT,
+ };
+
+ return tc_qdisc_modify(rth, ifindex, RTM_DELQDISC, 0, &opt);
+}
+
+static int remove_ingress(struct rtnl_handle *rth, int ifindex)
+{
+ struct qdisc_opt opt = {
+ .handle = 0xffff0000,
+ .parent = TC_H_INGRESS,
+ };
+
+ return tc_qdisc_modify(rth, ifindex, RTM_DELQDISC, 0, &opt);
+}
+
+static int remove_htb_ifb(struct rtnl_handle *rth, int ifindex, int priority)
+{
+ struct qdisc_opt opt = {
+ .handle = 0x00010001 + priority,
+ .parent = 0x00010000,
+ };
+
+ return tc_qdisc_modify(rth, conf_ifb_ifindex, RTM_DELTCLASS, 0, &opt);
+}
+
+int install_limiter(struct ppp_t *ppp, int down_speed, int down_burst, int up_speed, int up_burst)
+{
+ struct rtnl_handle rth;
+ int r;
+
+ if (rtnl_open(&rth, 0)) {
+ log_ppp_error("shaper: cannot open rtnetlink\n");
+ return -1;
+ }
+
+ down_speed = down_speed * 1000 / 8;
+ down_burst = down_burst ? down_burst : conf_down_burst_factor * down_speed;
+ up_speed = up_speed * 1000 / 8;
+ up_burst = up_burst ? up_burst : conf_up_burst_factor * up_speed;
+
+ if (conf_down_limiter == LIM_TBF)
+ r = install_tbf(&rth, ppp->ifindex, down_speed, down_burst);
+ else
+ r = install_htb(&rth, ppp->ifindex, down_speed, down_burst);
+
+ if (conf_up_limiter == LIM_POLICE)
+ r = install_police(&rth, ppp->ifindex, up_speed, up_burst);
+ else
+ r = install_htb_ifb(&rth, ppp->ifindex, ppp->unit_idx + 1, down_speed, down_burst);
+
+ rtnl_close(&rth);
+
+ return r;
+}
+
+int remove_limiter(struct ppp_t *ppp)
+{
+ struct rtnl_handle rth;
+
+ if (rtnl_open(&rth, 0)) {
+ log_ppp_error("shaper: cannot open rtnetlink\n");
+ return -1;
+ }
+
+ remove_root(&rth, ppp->ifindex);
+ remove_ingress(&rth, ppp->ifindex);
+
+ if (conf_up_limiter == LIM_HTB)
+ remove_htb_ifb(&rth, ppp->ifindex, ppp->unit_idx + 1);
+
+ return 0;
+}
+
+int init_ifb(const char *name)
+{
+ struct rtnl_handle rth;
+ struct rtattr *tail;
+ struct ifreq ifr;
+ int r;
+
+ struct qdisc_opt opt = {
+ .kind = "htb",
+ .handle = 0x00010000,
+ .parent = TC_H_ROOT,
+ .quantum = conf_r2q,
+ .qdisc = qdisc_htb_root,
+ };
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, name);
+
+ if (ioctl(sock_fd, SIOCGIFINDEX, &ifr)) {
+ log_emerg("shaper: ioctl(SIOCGIFINDEX): %s\n", strerror(errno));
+ return -1;
+ }
+
+ conf_ifb_ifindex = ifr.ifr_ifindex;
+
+ ifr.ifr_flags |= IFF_UP;
+
+ if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr)) {
+ log_emerg("shaper: ioctl(SIOCSIFINDEX): %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (rtnl_open(&rth, 0)) {
+ log_emerg("shaper: cannot open rtnetlink\n");
+ return -1;
+ }
+
+ tc_qdisc_modify(&rth, conf_ifb_ifindex, RTM_DELQDISC, 0, &opt);
+
+ r = tc_qdisc_modify(&rth, conf_ifb_ifindex, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_REPLACE, &opt);
+ if (r)
+ goto out;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_EXCL|NLM_F_CREATE;
+ req.n.nlmsg_type = RTM_NEWTFILTER;
+ req.t.tcm_family = AF_UNSPEC;
+ req.t.tcm_ifindex = conf_ifb_ifindex;
+ req.t.tcm_handle = 1;
+ req.t.tcm_parent = 0x00010000;
+ req.t.tcm_info = TC_H_MAKE(1 << 16, ntohs(ETH_P_IP));
+
+ addattr_l(&req.n, sizeof(req), TCA_KIND, "flow", 5);
+
+ tail = NLMSG_TAIL(&req.n);
+ addattr_l(&req.n, 4096, TCA_OPTIONS, NULL, 0);
+ addattr32(&req.n, 4096, TCA_FLOW_KEYS, 1 << FLOW_KEY_PRIORITY);
+ addattr32(&req.n, 4096, TCA_FLOW_MODE, FLOW_MODE_MAP);
+ tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail;
+
+ r = rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL, 0);
+
+out:
+ rtnl_close(&rth);
+
+ return r;
+}
diff --git a/accel-pppd/shaper/shaper.c b/accel-pppd/shaper/shaper.c
new file mode 100644
index 0000000..1363b98
--- /dev/null
+++ b/accel-pppd/shaper/shaper.c
@@ -0,0 +1,929 @@
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <pthread.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"
+
+#include "shaper.h"
+#include "tc_core.h"
+
+#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
+double conf_down_burst_factor = 0.1;
+double conf_up_burst_factor = 1;
+double conf_latency = 0.05;
+int conf_mpu = 0;
+int conf_quantum = 1500;
+int conf_r2q = 10;
+int conf_ifb_ifindex;
+
+int conf_up_limiter = LIM_POLICE;
+int conf_down_limiter = LIM_TBF;
+
+static int temp_down_speed;
+static int temp_up_speed;
+
+static pthread_rwlock_t shaper_lock = PTHREAD_RWLOCK_INITIALIZER;
+static LIST_HEAD(shaper_list);
+
+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 = 0;
+
+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 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("shaper: 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 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);
+ }
+ }
+}
+
+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 || id == 0)
+ 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);
+ }
+}
+
+#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 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_burst;
+ }
+
+ if (down_speed > 0 && up_speed > 0) {
+ if (!install_limiter(ev->ppp, down_speed, down_burst, up_speed, up_burst)) {
+ if (conf_verbose)
+ log_ppp_info2("shaper: 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("shaper: removed shaper\n");
+ remove_limiter(ev->ppp);
+ }
+ 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_limiter(ev->ppp)) {
+ ev->res = -1;
+ return;
+ }
+
+ if (pd->down_speed > 0 || pd->up_speed > 0) {
+ if (install_limiter(ev->ppp, 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("shaper: changed shaper %i/%i (Kbit)\n", pd->down_speed, pd->up_speed);
+ }
+ } else {
+ if (conf_verbose)
+ log_ppp_info2("shaper: 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 = 0;
+ struct time_range_pd_t *tr_pd;
+
+ 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);
+
+ tr_pd = get_tr_pd(pd, tr_id);
+ tr_pd->down_speed = down_speed;
+ tr_pd->down_burst = down_burst;
+ tr_pd->up_speed = up_speed;
+ tr_pd->up_burst = up_burst;
+
+ 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 = down_speed;
+ pd->up_speed = up_speed;
+ }
+
+ if (pd->down_speed > 0 && pd->up_speed > 0) {
+ if (!install_limiter(ev->ppp, down_speed, down_burst, up_speed, up_burst)) {
+ if (conf_verbose)
+ log_ppp_info2("shaper: installed shaper %i/%i (Kbit)\n", down_speed, up_speed);
+ }
+ }
+}
+
+static void ev_ppp_pre_up(struct ppp_t *ppp)
+{
+ struct shaper_pd_t *pd = find_pd(ppp, 1);
+ if (!pd)
+ return;
+
+ 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;
+ if (!install_limiter(ppp, temp_down_speed, 0, temp_up_speed, 0)) {
+ if (conf_verbose)
+ log_ppp_info2("shaper: installed shaper %i/%i (Kbit)\n", temp_down_speed, temp_up_speed);
+ }
+ }
+}
+
+static void ev_ppp_finishing(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);
+
+ if (pd->down_speed || pd->up_speed)
+ remove_limiter(ppp);
+
+ _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->down_speed || pd->up_speed)
+ remove_limiter(pd->ppp);
+
+ if (pd->temp_down_speed || pd->temp_up_speed) {
+ pd->down_speed = pd->temp_down_speed;
+ pd->up_speed = pd->temp_up_speed;
+ install_limiter(pd->ppp, pd->temp_down_speed, 0, pd->temp_up_speed, 0);
+ } else if (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;
+ install_limiter(pd->ppp, pd->cur_tr->down_speed, pd->cur_tr->down_burst, pd->cur_tr->up_speed, pd->cur_tr->up_burst);
+ } else {
+ pd->down_speed = 0;
+ pd->up_speed = 0;
+ }
+}
+
+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_limiter(pd->ppp);
+
+ if (pd->cur_tr) {
+ pd->down_speed = pd->cur_tr->down_speed;
+ pd->up_speed = pd->cur_tr->up_speed;
+ install_limiter(pd->ppp, pd->cur_tr->down_speed, pd->cur_tr->down_burst, pd->cur_tr->up_speed, pd->cur_tr->up_burst);
+ } else {
+ pd->down_speed = 0;
+ pd->up_speed = 0;
+ }
+}
+
+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;
+
+ if (pd->ppp->terminating)
+ return;
+
+ 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_limiter(pd->ppp);
+ }
+
+ 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_limiter(pd->ppp, 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("shaper: changed shaper %i/%i (Kbit)\n", pd->cur_tr->down_speed, pd->cur_tr->up_speed);
+ }
+ } else
+ if (conf_verbose)
+ log_ppp_info2("shaper: 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("shaper: 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("shaper: 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("shaper");
+ 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 && ts <= r->end.expire_tv.tv_sec + 24*60*60)
+ 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("shaper: 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
+ if (triton_module_loaded("radius")) {
+ opt = conf_get_opt("shaper", "vendor");
+ if (opt)
+ conf_vendor = parse_vendor_opt(opt);
+
+ opt = conf_get_opt("shaper", "attr");
+ if (opt) {
+ conf_attr_down = parse_attr_opt(opt);
+ conf_attr_up = parse_attr_opt(opt);
+ }
+
+ opt = conf_get_opt("shaper", "attr-down");
+ if (opt)
+ conf_attr_down = parse_attr_opt(opt);
+
+ opt = conf_get_opt("shaper", "attr-up");
+ if (opt)
+ conf_attr_up = parse_attr_opt(opt);
+
+ if (conf_attr_up <= 0 || conf_attr_down <= 0) {
+ log_emerg("shaper: incorrect attribute(s), tbf disabled...\n");
+ return;
+ }
+ }
+#endif
+
+ opt = conf_get_opt("shaper", "burst-factor");
+ if (opt) {
+ conf_down_burst_factor = strtod(opt, NULL);
+ conf_up_burst_factor = conf_down_burst_factor * 10;
+ }
+
+ opt = conf_get_opt("shaper", "down-burst-factor");
+ if (opt)
+ conf_down_burst_factor = strtod(opt, NULL);
+
+ opt = conf_get_opt("shaper", "up-burst-factor");
+ if (opt)
+ conf_up_burst_factor = strtod(opt, NULL);
+
+ opt = conf_get_opt("shaper", "latency");
+ if (opt && atoi(opt) > 0)
+ conf_latency = (double)atoi(opt) / 1000;
+
+ opt = conf_get_opt("shaper", "mpu");
+ if (opt && atoi(opt) >= 0)
+ conf_mpu = atoi(opt);
+
+ opt = conf_get_opt("shaper", "r2q");
+ if (opt && atoi(opt) >= 0)
+ conf_r2q = atoi(opt);
+
+ opt = conf_get_opt("shaper", "quantum");
+ if (opt && atoi(opt) >= 0)
+ conf_quantum = atoi(opt);
+
+ opt = conf_get_opt("shaper", "up-limiter");
+ if (opt) {
+ if (!strcmp(opt, "police"))
+ conf_up_limiter = LIM_POLICE;
+ else if (!strcmp(opt, "htb"))
+ conf_up_limiter = LIM_HTB;
+ else
+ log_error("shaper: unknown upstream limiter '%s'\n", opt);
+ }
+
+ opt = conf_get_opt("shaper", "down-limiter");
+ if (opt) {
+ if (!strcmp(opt, "tbf"))
+ conf_down_limiter = LIM_TBF;
+ else if (!strcmp(opt, "htb"))
+ conf_down_limiter = LIM_HTB;
+ else
+ log_error("shaper: unknown downstream limiter '%s'\n", opt);
+ }
+
+ if (conf_up_limiter == LIM_HTB && !conf_ifb_ifindex) {
+ log_warn("shaper: requested 'htb' upstream limiter, but no 'ifb' specified, falling back to police...\n");
+ conf_up_limiter = LIM_POLICE;
+ }
+
+ opt = conf_get_opt("shaper", "verbose");
+ if (opt && atoi(opt) > 0)
+ conf_verbose = 1;
+
+ triton_context_call(&shaper_ctx, (triton_event_func)load_time_ranges, NULL);
+}
+
+static void init(void)
+{
+ const char *opt;
+
+ tc_core_init();
+
+ opt = conf_get_opt("shaper", "ifb");
+ if (opt && init_ifb(opt))
+ _exit(0);
+
+ triton_context_register(&shaper_ctx, NULL);
+ triton_context_wakeup(&shaper_ctx);
+
+ load_config();
+
+#ifdef RADIUS
+ if (triton_module_loaded("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_PPP_PRE_UP, (triton_event_func)ev_ppp_pre_up);
+ triton_event_register_handler(EV_PPP_FINISHING, (triton_event_func)ev_ppp_finishing);
+ //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);
+}
+
+DEFINE_INIT(100, init);
diff --git a/accel-pppd/shaper/shaper.h b/accel-pppd/shaper/shaper.h
new file mode 100644
index 0000000..2b0514b
--- /dev/null
+++ b/accel-pppd/shaper/shaper.h
@@ -0,0 +1,23 @@
+#ifndef __SHAPER_H
+#define __SHAPER_H
+
+#define LIM_POLICE 0
+#define LIM_TBF 1
+#define LIM_HTB 2
+
+extern int conf_up_limiter;
+extern int conf_down_limiter;
+
+extern double conf_down_burst_factor;
+extern double conf_up_burst_factor;
+extern double conf_latency;
+extern int conf_mpu;
+extern int conf_quantum;
+extern int conf_r2q;
+extern int conf_ifb_ifindex;
+
+int install_limiter(struct ppp_t *ppp, int down_speed, int down_burst, int up_speed, int up_burst);
+int remove_limiter(struct ppp_t *ppp);
+int init_ifb(const char *);
+
+#endif
diff --git a/accel-pppd/shaper/tc_core.c b/accel-pppd/shaper/tc_core.c
new file mode 100644
index 0000000..9a0ff39
--- /dev/null
+++ b/accel-pppd/shaper/tc_core.c
@@ -0,0 +1,211 @@
+/*
+ * tc_core.c TC core library.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <math.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "tc_core.h"
+#include <linux/atm.h>
+
+static double tick_in_usec = 1;
+static double clock_factor = 1;
+
+int tc_core_time2big(unsigned time)
+{
+ __u64 t = time;
+
+ t *= tick_in_usec;
+ return (t >> 32) != 0;
+}
+
+
+unsigned tc_core_time2tick(unsigned time)
+{
+ return time*tick_in_usec;
+}
+
+unsigned tc_core_tick2time(unsigned tick)
+{
+ return tick/tick_in_usec;
+}
+
+unsigned tc_core_time2ktime(unsigned time)
+{
+ return time * clock_factor;
+}
+
+unsigned tc_core_ktime2time(unsigned ktime)
+{
+ return ktime / clock_factor;
+}
+
+unsigned tc_calc_xmittime(unsigned rate, unsigned size)
+{
+ return tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/rate));
+}
+
+unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks)
+{
+ return ((double)rate*tc_core_tick2time(ticks))/TIME_UNITS_PER_SEC;
+}
+
+/*
+ * The align to ATM cells is used for determining the (ATM) SAR
+ * alignment overhead at the ATM layer. (SAR = Segmentation And
+ * Reassembly). This is for example needed when scheduling packet on
+ * an ADSL connection. Note that the extra ATM-AAL overhead is _not_
+ * included in this calculation. This overhead is added in the kernel
+ * before doing the rate table lookup, as this gives better precision
+ * (as the table will always be aligned for 48 bytes).
+ * --Hawk, d.7/11-2004. <hawk@diku.dk>
+ */
+unsigned tc_align_to_atm(unsigned size)
+{
+ int linksize, cells;
+ cells = size / ATM_CELL_PAYLOAD;
+ if ((size % ATM_CELL_PAYLOAD) > 0)
+ cells++;
+
+ linksize = cells * ATM_CELL_SIZE; /* Use full cell size to add ATM tax */
+ return linksize;
+}
+
+unsigned tc_adjust_size(unsigned sz, unsigned mpu, enum link_layer linklayer)
+{
+ if (sz < mpu)
+ sz = mpu;
+
+ switch (linklayer) {
+ case LINKLAYER_ATM:
+ return tc_align_to_atm(sz);
+ case LINKLAYER_ETHERNET:
+ default:
+ // No size adjustments on Ethernet
+ return sz;
+ }
+}
+
+/*
+ rtab[pkt_len>>cell_log] = pkt_xmit_time
+ */
+
+int tc_calc_rtable(struct tc_ratespec *r, __u32 *rtab,
+ int cell_log, unsigned mtu,
+ enum link_layer linklayer)
+{
+ 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);
+ rtab[i] = tc_calc_xmittime(bps, sz);
+ }
+
+ r->cell_align=-1; // Due to the sz calc
+ r->cell_log=cell_log;
+ return cell_log;
+}
+
+/*
+ stab[pkt_len>>cell_log] = pkt_xmit_size>>size_log
+ */
+
+int tc_calc_size_table(struct tc_sizespec *s, __u16 **stab)
+{
+ int i;
+ enum link_layer linklayer = s->linklayer;
+ unsigned int sz;
+
+ if (linklayer <= LINKLAYER_ETHERNET && s->mpu == 0) {
+ /* don't need data table in this case (only overhead set) */
+ s->mtu = 0;
+ s->tsize = 0;
+ s->cell_log = 0;
+ s->cell_align = 0;
+ *stab = NULL;
+ return 0;
+ }
+
+ if (s->mtu == 0)
+ s->mtu = 2047;
+ if (s->tsize == 0)
+ s->tsize = 512;
+
+ s->cell_log = 0;
+ while ((s->mtu >> s->cell_log) > s->tsize - 1)
+ s->cell_log++;
+
+ *stab = malloc(s->tsize * sizeof(__u16));
+ if (!*stab)
+ return -1;
+
+again:
+ for (i = s->tsize - 1; i >= 0; i--) {
+ sz = tc_adjust_size((i + 1) << s->cell_log, s->mpu, linklayer);
+ if ((sz >> s->size_log) > UINT16_MAX) {
+ s->size_log++;
+ goto again;
+ }
+ (*stab)[i] = sz >> s->size_log;
+ }
+
+ s->cell_align = -1; // Due to the sz calc
+ return 0;
+}
+
+int tc_core_init()
+{
+ FILE *fp;
+ __u32 clock_res;
+ __u32 t2us;
+ __u32 us2t;
+
+ fp = fopen("/proc/net/psched", "r");
+ if (fp == NULL)
+ return -1;
+
+ if (fscanf(fp, "%08x%08x%08x", &t2us, &us2t, &clock_res) != 3) {
+ 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;
+}
diff --git a/accel-pppd/shaper/tc_core.h b/accel-pppd/shaper/tc_core.h
new file mode 100644
index 0000000..cb85cc4
--- /dev/null
+++ b/accel-pppd/shaper/tc_core.h
@@ -0,0 +1,33 @@
+#ifndef _TC_CORE_H_
+#define _TC_CORE_H_ 1
+
+#include <asm/types.h>
+#include <linux/pkt_sched.h>
+
+#define TIME_UNITS_PER_SEC 1000000
+
+enum link_layer {
+ LINKLAYER_UNSPEC,
+ LINKLAYER_ETHERNET,
+ LINKLAYER_ATM,
+};
+
+
+int tc_core_time2big(unsigned time);
+unsigned tc_core_time2tick(unsigned time);
+unsigned tc_core_tick2time(unsigned tick);
+unsigned tc_core_time2ktime(unsigned time);
+unsigned tc_core_ktime2time(unsigned ktime);
+unsigned tc_calc_xmittime(unsigned rate, unsigned size);
+unsigned tc_calc_xmitsize(unsigned rate, unsigned ticks);
+int tc_calc_rtable(struct tc_ratespec *r, __u32 *rtab,
+ int cell_log, unsigned mtu, enum link_layer link_layer);
+int tc_calc_size_table(struct tc_sizespec *s, __u16 **stab);
+
+int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est);
+
+int tc_core_init(void);
+
+extern struct rtnl_handle g_rth;
+
+#endif