123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- /*
- Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #define _GNU_SOURCE
- #include <errno.h>
- #include <error.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <arpa/inet.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <net/if.h>
- #include <linux/if_ether.h>
- #include <linux/pkt_cls.h>
- #include <linux/pkt_sched.h>
- #include <linux/rtnetlink.h>
- #include <netlink/msg.h>
- #include <netlink/attr.h>
- #include <netlink/socket.h>
- static struct nl_cb *cb;
- static struct nl_sock *sock;
- static double ticks;
- static unsigned ifindex;
- static bool nlexpect;
- static int nlerror;
- static inline void exit_errno(const char *message) {
- error(1, errno, "error: %s", message);
- }
- static inline void warn_errno(const char *message) {
- error(0, errno, "warning: %s", message);
- }
- static void read_psched(void) {
- uint32_t clock_res;
- uint32_t t2us;
- uint32_t us2t;
- FILE *f = fopen("/proc/net/psched", "r");
- if (!f || fscanf(f, "%08x %08x %08x", &t2us, &us2t, &clock_res) != 3)
- exit_errno("error reading /proc/net/psched");
- fclose(f);
- /* compatibility hack from iproute... */
- if (clock_res == 1000000000)
- t2us = us2t;
- ticks = (double)t2us / us2t * clock_res;
- }
- static struct nl_msg * prepare_tcmsg(int type, int flags, uint32_t parent, uint32_t handle, uint32_t info) {
- struct nl_msg *msg = nlmsg_alloc_simple(type, flags);
- if (!msg)
- exit_errno("nlmsg_alloc_simple");
- struct tcmsg tcmsg;
- memset(&tcmsg, 0, sizeof(tcmsg));
- tcmsg.tcm_family = AF_UNSPEC;
- tcmsg.tcm_ifindex = ifindex;
- tcmsg.tcm_parent = parent;
- tcmsg.tcm_handle = handle;
- tcmsg.tcm_info = info;
- nlmsg_append(msg, &tcmsg, sizeof(tcmsg), NLMSG_ALIGNTO);
- return msg;
- }
- static int error_handler(struct sockaddr_nl *nla __attribute__((unused)), struct nlmsgerr *nlerr, void *arg __attribute__((unused))) {
- if (!nlexpect || (nlerr->error != -ENOENT && nlerr->error != -EINVAL))
- nlerror = -nlerr->error;
- return NL_STOP;
- }
- static bool do_send(struct nl_msg *msg, bool expect) {
- nlerror = 0;
- nlexpect = expect;
- nl_send_auto_complete(sock, msg);
- nlmsg_free(msg);
- nl_wait_for_ack(sock);
- if (nlerror) {
- error(0, nlerror, "netlink");
- errno = nlerror;
- return false;
- }
- return true;
- }
- static inline unsigned get_xmittime(double rate, unsigned size) {
- return ticks * (size/rate);
- }
- static void complete_rate(struct tc_ratespec *r, uint32_t rtab[256]) {
- r->linklayer = TC_LINKLAYER_ETHERNET;
- r->cell_align = -1;
- r->cell_log = 3;
- unsigned i;
- for (i = 0; i < 256; i++)
- rtab[i] = get_xmittime(r->rate, (i + 1) << 3);
- }
- static void do_ingress(double rate) {
- if (!do_send(prepare_tcmsg(RTM_DELQDISC, 0, TC_H_INGRESS, 0xffff0000, 0), true))
- return;
- if (rate < 0)
- return;
- struct nl_msg *msg = prepare_tcmsg(RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL, TC_H_INGRESS, 0xffff0000, 0);
- nla_put_string(msg, TCA_KIND, "ingress");
- if (!do_send(msg, false))
- return;
- msg = prepare_tcmsg(RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_EXCL, 0xffff0000, 0, TC_H_MAKE(0, htons(ETH_P_ALL)));
- const unsigned buffer = 10240;
- struct tc_police p;
- memset(&p, 0, sizeof(p));
- /* Range check has been done in main() */
- p.rate.rate = rate;
- p.burst = get_xmittime(p.rate.rate, buffer);
- p.action = TC_POLICE_SHOT;
- uint32_t rtab[256];
- complete_rate(&p.rate, rtab);
- nla_put_string(msg, TCA_KIND, "basic");
- struct nlattr *opts = nla_nest_start(msg, TCA_OPTIONS);
- struct nlattr *police = nla_nest_start(msg, TCA_BASIC_POLICE);
- nla_put(msg, TCA_POLICE_TBF, sizeof(p), &p);
- nla_put(msg, TCA_POLICE_RATE, sizeof(rtab), rtab);
- nla_nest_end(msg, police);
- nla_nest_end(msg, opts);
- do_send(msg, false);
- }
- static void do_egress(double rate) {
- if (!do_send(prepare_tcmsg(RTM_DELQDISC, 0, TC_H_ROOT, 0, 0), true))
- return;
- if (rate < 0)
- return;
- struct nl_msg *msg = prepare_tcmsg(RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL, TC_H_ROOT, 0, 0);
- const unsigned buffer = 2048;
- struct tc_tbf_qopt opt;
- memset(&opt, 0, sizeof(opt));
- /* Range check has been done in main() */
- opt.rate.rate = rate;
- opt.limit = 0.05*rate + buffer;
- opt.buffer = get_xmittime(opt.rate.rate, buffer);
- uint32_t rtab[256];
- complete_rate(&opt.rate, rtab);
- nla_put_string(msg, TCA_KIND, "tbf");
- struct nlattr *opts = nla_nest_start(msg, TCA_OPTIONS);
- nla_put(msg, TCA_TBF_PARMS, sizeof(opt), &opt);
- nla_put(msg, TCA_TBF_BURST, sizeof(buffer), &buffer);
- nla_put(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
- nla_nest_end(msg, opts);
- do_send(msg, false);
- }
- static inline void usage(void) {
- fprintf(stderr, "Usage: gluon-simple-tc <interface> <ingress Kbit/s>|- <egress Kbit/s>|-\n");
- exit(1);
- }
- static inline void maxrate(void) {
- error(1, 0, "error: maximum allowed rate it about 2^25 Kbit/s");
- }
- int main(int argc, char *argv[]) {
- if (argc != 4)
- usage();
- double ingress = -1, egress = -1;
- char *end;
- ifindex = if_nametoindex(argv[1]);
- if (!ifindex)
- error(1, 0, "invalid interface: %s", argv[1]);
- if (strcmp(argv[2], "-") != 0) {
- ingress = strtod(argv[2], &end);
- if (*end || ingress < 0)
- usage();
- ingress *= 125;
- if (ingress >= (1ull << 32))
- maxrate();
- }
- if (strcmp(argv[3], "-") != 0) {
- egress = strtod(argv[3], &end);
- if (*end || egress < 0)
- usage();
- egress *= 125;
- if (egress >= (1ull << 32))
- maxrate();
- }
- read_psched();
- cb = nl_cb_alloc(NL_CB_DEFAULT);
- nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL);
- sock = nl_socket_alloc_cb(cb);
- if (!sock)
- exit_errno("nl_socket_alloc");
- if (nl_connect(sock, NETLINK_ROUTE))
- exit_errno("nl_connect");
- do_ingress(ingress);
- do_egress(egress);
- nl_socket_free(sock);
- nl_cb_put(cb);
- return 0;
- }
|