Home | History | Annotate | Download | only in tc
      1 /*
      2  * q_tbf.c		TBF.
      3  *
      4  *		This program is free software; you can redistribute it and/or
      5  *		modify it under the terms of the GNU General Public License
      6  *		as published by the Free Software Foundation; either version
      7  *		2 of the License, or (at your option) any later version.
      8  *
      9  * Authors:	Alexey Kuznetsov, <kuznet (at) ms2.inr.ac.ru>
     10  *
     11  */
     12 
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 #include <unistd.h>
     16 #include <syslog.h>
     17 #include <fcntl.h>
     18 #include <sys/socket.h>
     19 #include <netinet/in.h>
     20 #include <arpa/inet.h>
     21 #include <string.h>
     22 
     23 #include "utils.h"
     24 #include "tc_util.h"
     25 
     26 static void explain(void)
     27 {
     28 	fprintf(stderr, "Usage: ... tbf limit BYTES burst BYTES[/BYTES] rate KBPS [ mtu BYTES[/BYTES] ]\n");
     29 	fprintf(stderr, "               [ peakrate KBPS ] [ latency TIME ] ");
     30 	fprintf(stderr, "[ overhead BYTES ] [ linklayer TYPE ]\n");
     31 }
     32 
     33 static void explain1(const char *arg, const char *val)
     34 {
     35 	fprintf(stderr, "tbf: illegal value for \"%s\": \"%s\"\n", arg, val);
     36 }
     37 
     38 
     39 static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
     40 {
     41 	int ok=0;
     42 	struct tc_tbf_qopt opt;
     43 	__u32 rtab[256];
     44 	__u32 ptab[256];
     45 	unsigned buffer=0, mtu=0, mpu=0, latency=0;
     46 	int Rcell_log=-1, Pcell_log = -1;
     47 	unsigned short overhead=0;
     48 	unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
     49 	struct rtattr *tail;
     50 	__u64 rate64 = 0, prate64 = 0;
     51 
     52 	memset(&opt, 0, sizeof(opt));
     53 
     54 	while (argc > 0) {
     55 		if (matches(*argv, "limit") == 0) {
     56 			NEXT_ARG();
     57 			if (opt.limit) {
     58 				fprintf(stderr, "tbf: duplicate \"limit\" specification\n");
     59 				return -1;
     60 			}
     61 			if (latency) {
     62 				fprintf(stderr, "tbf: specifying both \"latency\" and \"limit\" is not allowed\n");
     63 				return -1;
     64 			}
     65 			if (get_size(&opt.limit, *argv)) {
     66 				explain1("limit", *argv);
     67 				return -1;
     68 			}
     69 			ok++;
     70 		} else if (matches(*argv, "latency") == 0) {
     71 			NEXT_ARG();
     72 			if (latency) {
     73 				fprintf(stderr, "tbf: duplicate \"latency\" specification\n");
     74 				return -1;
     75 			}
     76 			if (opt.limit) {
     77 				fprintf(stderr, "tbf: specifying both \"limit\" and \"/latency\" is not allowed\n");
     78 				return -1;
     79 			}
     80 			if (get_time(&latency, *argv)) {
     81 				explain1("latency", *argv);
     82 				return -1;
     83 			}
     84 			ok++;
     85 		} else if (matches(*argv, "burst") == 0 ||
     86 			strcmp(*argv, "buffer") == 0 ||
     87 			strcmp(*argv, "maxburst") == 0) {
     88 			const char *parm_name = *argv;
     89 			NEXT_ARG();
     90 			if (buffer) {
     91 				fprintf(stderr, "tbf: duplicate \"buffer/burst/maxburst\" specification\n");
     92 				return -1;
     93 			}
     94 			if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) {
     95 				explain1(parm_name, *argv);
     96 				return -1;
     97 			}
     98 			ok++;
     99 		} else if (strcmp(*argv, "mtu") == 0 ||
    100 			   strcmp(*argv, "minburst") == 0) {
    101 			const char *parm_name = *argv;
    102 			NEXT_ARG();
    103 			if (mtu) {
    104 				fprintf(stderr, "tbf: duplicate \"mtu/minburst\" specification\n");
    105 				return -1;
    106 			}
    107 			if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) {
    108 				explain1(parm_name, *argv);
    109 				return -1;
    110 			}
    111 			ok++;
    112 		} else if (strcmp(*argv, "mpu") == 0) {
    113 			NEXT_ARG();
    114 			if (mpu) {
    115 				fprintf(stderr, "tbf: duplicate \"mpu\" specification\n");
    116 				return -1;
    117 			}
    118 			if (get_size(&mpu, *argv)) {
    119 				explain1("mpu", *argv);
    120 				return -1;
    121 			}
    122 			ok++;
    123 		} else if (strcmp(*argv, "rate") == 0) {
    124 			NEXT_ARG();
    125 			if (rate64) {
    126 				fprintf(stderr, "tbf: duplicate \"rate\" specification\n");
    127 				return -1;
    128 			}
    129 			if (get_rate64(&rate64, *argv)) {
    130 				explain1("rate", *argv);
    131 				return -1;
    132 			}
    133 			ok++;
    134 		} else if (matches(*argv, "peakrate") == 0) {
    135 			NEXT_ARG();
    136 			if (prate64) {
    137 				fprintf(stderr, "tbf: duplicate \"peakrate\" specification\n");
    138 				return -1;
    139 			}
    140 			if (get_rate64(&prate64, *argv)) {
    141 				explain1("peakrate", *argv);
    142 				return -1;
    143 			}
    144 			ok++;
    145 		} else if (matches(*argv, "overhead") == 0) {
    146 			NEXT_ARG();
    147 			if (overhead) {
    148 				fprintf(stderr, "tbf: duplicate \"overhead\" specification\n");
    149 				return -1;
    150 			}
    151 			if (get_u16(&overhead, *argv, 10)) {
    152 				explain1("overhead", *argv); return -1;
    153 			}
    154 		} else if (matches(*argv, "linklayer") == 0) {
    155 			NEXT_ARG();
    156 			if (get_linklayer(&linklayer, *argv)) {
    157 				explain1("linklayer", *argv); return -1;
    158 			}
    159 		} else if (strcmp(*argv, "help") == 0) {
    160 			explain();
    161 			return -1;
    162 		} else {
    163 			fprintf(stderr, "tbf: unknown parameter \"%s\"\n", *argv);
    164 			explain();
    165 			return -1;
    166 		}
    167 		argc--; argv++;
    168 	}
    169 
    170         int verdict = 0;
    171 
    172         /* Be nice to the user: try to emit all error messages in
    173          * one go rather than reveal one more problem when a
    174          * previous one has been fixed.
    175          */
    176 	if (rate64 == 0) {
    177 		fprintf(stderr, "tbf: the \"rate\" parameter is mandatory.\n");
    178 		verdict = -1;
    179 	}
    180 	if (!buffer) {
    181 		fprintf(stderr, "tbf: the \"burst\" parameter is mandatory.\n");
    182 		verdict = -1;
    183 	}
    184 	if (prate64) {
    185 		if (!mtu) {
    186 			fprintf(stderr, "tbf: when \"peakrate\" is specified, \"mtu\" must also be specified.\n");
    187 			verdict = -1;
    188 		}
    189 	}
    190 
    191 	if (opt.limit == 0 && latency == 0) {
    192 		fprintf(stderr, "tbf: either \"limit\" or \"latency\" is required.\n");
    193 		verdict = -1;
    194 	}
    195 
    196         if (verdict != 0) {
    197                 explain();
    198                 return verdict;
    199         }
    200 
    201 	opt.rate.rate = (rate64 >= (1ULL << 32)) ? ~0U : rate64;
    202 	opt.peakrate.rate = (prate64 >= (1ULL << 32)) ? ~0U : prate64;
    203 
    204 	if (opt.limit == 0) {
    205 		double lim = rate64*(double)latency/TIME_UNITS_PER_SEC + buffer;
    206 		if (prate64) {
    207 			double lim2 = prate64*(double)latency/TIME_UNITS_PER_SEC + mtu;
    208 			if (lim2 < lim)
    209 				lim = lim2;
    210 		}
    211 		opt.limit = lim;
    212 	}
    213 
    214 	opt.rate.mpu      = mpu;
    215 	opt.rate.overhead = overhead;
    216 	if (tc_calc_rtable(&opt.rate, rtab, Rcell_log, mtu, linklayer) < 0) {
    217 		fprintf(stderr, "tbf: failed to calculate rate table.\n");
    218 		return -1;
    219 	}
    220 	opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer);
    221 
    222 	if (opt.peakrate.rate) {
    223 		opt.peakrate.mpu      = mpu;
    224 		opt.peakrate.overhead = overhead;
    225 		if (tc_calc_rtable(&opt.peakrate, ptab, Pcell_log, mtu, linklayer) < 0) {
    226 			fprintf(stderr, "tbf: failed to calculate peak rate table.\n");
    227 			return -1;
    228 		}
    229 		opt.mtu = tc_calc_xmittime(opt.peakrate.rate, mtu);
    230 	}
    231 
    232 	tail = NLMSG_TAIL(n);
    233 	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
    234 	addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt));
    235 	addattr_l(n, 2124, TCA_TBF_BURST, &buffer, sizeof(buffer));
    236 	if (rate64 >= (1ULL << 32))
    237 		addattr_l(n, 2124, TCA_TBF_RATE64, &rate64, sizeof(rate64));
    238 	addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024);
    239 	if (opt.peakrate.rate) {
    240 		if (prate64 >= (1ULL << 32))
    241 			addattr_l(n, 3124, TCA_TBF_PRATE64, &prate64, sizeof(prate64));
    242 		addattr_l(n, 3224, TCA_TBF_PBURST, &mtu, sizeof(mtu));
    243 		addattr_l(n, 4096, TCA_TBF_PTAB, ptab, 1024);
    244 	}
    245 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
    246 	return 0;
    247 }
    248 
    249 static int tbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
    250 {
    251 	struct rtattr *tb[TCA_TBF_MAX+1];
    252 	struct tc_tbf_qopt *qopt;
    253 	unsigned int linklayer;
    254 	double buffer, mtu;
    255 	double latency;
    256 	__u64 rate64 = 0, prate64 = 0;
    257 	SPRINT_BUF(b1);
    258 	SPRINT_BUF(b2);
    259 	SPRINT_BUF(b3);
    260 
    261 	if (opt == NULL)
    262 		return 0;
    263 
    264 	parse_rtattr_nested(tb, TCA_TBF_MAX, opt);
    265 
    266 	if (tb[TCA_TBF_PARMS] == NULL)
    267 		return -1;
    268 
    269 	qopt = RTA_DATA(tb[TCA_TBF_PARMS]);
    270 	if (RTA_PAYLOAD(tb[TCA_TBF_PARMS])  < sizeof(*qopt))
    271 		return -1;
    272 	rate64 = qopt->rate.rate;
    273 	if (tb[TCA_TBF_RATE64] &&
    274 	    RTA_PAYLOAD(tb[TCA_TBF_RATE64]) >= sizeof(rate64))
    275 		rate64 = rta_getattr_u64(tb[TCA_TBF_RATE64]);
    276 	fprintf(f, "rate %s ", sprint_rate(rate64, b1));
    277 	buffer = tc_calc_xmitsize(rate64, qopt->buffer);
    278 	if (show_details) {
    279 		fprintf(f, "burst %s/%u mpu %s ", sprint_size(buffer, b1),
    280 			1<<qopt->rate.cell_log, sprint_size(qopt->rate.mpu, b2));
    281 	} else {
    282 		fprintf(f, "burst %s ", sprint_size(buffer, b1));
    283 	}
    284 	if (show_raw)
    285 		fprintf(f, "[%08x] ", qopt->buffer);
    286 	prate64 = qopt->peakrate.rate;
    287 	if (tb[TCA_TBF_PRATE64] &&
    288 	    RTA_PAYLOAD(tb[TCA_TBF_PRATE64]) >= sizeof(prate64))
    289 		prate64 = rta_getattr_u64(tb[TCA_TBF_PRATE64]);
    290 	if (prate64) {
    291 		fprintf(f, "peakrate %s ", sprint_rate(prate64, b1));
    292 		if (qopt->mtu || qopt->peakrate.mpu) {
    293 			mtu = tc_calc_xmitsize(prate64, qopt->mtu);
    294 			if (show_details) {
    295 				fprintf(f, "mtu %s/%u mpu %s ", sprint_size(mtu, b1),
    296 					1<<qopt->peakrate.cell_log, sprint_size(qopt->peakrate.mpu, b2));
    297 			} else {
    298 				fprintf(f, "minburst %s ", sprint_size(mtu, b1));
    299 			}
    300 			if (show_raw)
    301 				fprintf(f, "[%08x] ", qopt->mtu);
    302 		}
    303 	}
    304 
    305 	latency = TIME_UNITS_PER_SEC*(qopt->limit/(double)rate64) - tc_core_tick2time(qopt->buffer);
    306 	if (prate64) {
    307 		double lat2 = TIME_UNITS_PER_SEC*(qopt->limit/(double)prate64) - tc_core_tick2time(qopt->mtu);
    308 		if (lat2 > latency)
    309 			latency = lat2;
    310 	}
    311 	if (latency >= 0.0)
    312 		fprintf(f, "lat %s ", sprint_time(latency, b1));
    313 	if (show_raw || latency < 0.0)
    314 		fprintf(f, "limit %s ", sprint_size(qopt->limit, b1));
    315 
    316 	if (qopt->rate.overhead) {
    317 		fprintf(f, "overhead %d", qopt->rate.overhead);
    318 	}
    319 	linklayer = (qopt->rate.linklayer & TC_LINKLAYER_MASK);
    320 	if (linklayer > TC_LINKLAYER_ETHERNET || show_details)
    321 		fprintf(f, "linklayer %s ", sprint_linklayer(linklayer, b3));
    322 
    323 	return 0;
    324 }
    325 
    326 struct qdisc_util tbf_qdisc_util = {
    327 	.id		= "tbf",
    328 	.parse_qopt	= tbf_parse_opt,
    329 	.print_qopt	= tbf_print_opt,
    330 };
    331