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 	while (argc > 0) {
     53 		if (matches(*argv, "limit") == 0) {
     54 			NEXT_ARG();
     55 			if (opt.limit) {
     56 				fprintf(stderr, "tbf: duplicate \"limit\" specification\n");
     57 				return -1;
     58 			}
     59 			if (latency) {
     60 				fprintf(stderr, "tbf: specifying both \"latency\" and \"limit\" is not allowed\n");
     61 				return -1;
     62 			}
     63 			if (get_size(&opt.limit, *argv)) {
     64 				explain1("limit", *argv);
     65 				return -1;
     66 			}
     67 			ok++;
     68 		} else if (matches(*argv, "latency") == 0) {
     69 			NEXT_ARG();
     70 			if (latency) {
     71 				fprintf(stderr, "tbf: duplicate \"latency\" specification\n");
     72 				return -1;
     73 			}
     74 			if (opt.limit) {
     75 				fprintf(stderr, "tbf: specifying both \"limit\" and \"/latency\" is not allowed\n");
     76 				return -1;
     77 			}
     78 			if (get_time(&latency, *argv)) {
     79 				explain1("latency", *argv);
     80 				return -1;
     81 			}
     82 			ok++;
     83 		} else if (matches(*argv, "burst") == 0 ||
     84 			strcmp(*argv, "buffer") == 0 ||
     85 			strcmp(*argv, "maxburst") == 0) {
     86 			const char *parm_name = *argv;
     87 
     88 			NEXT_ARG();
     89 			if (buffer) {
     90 				fprintf(stderr, "tbf: duplicate \"buffer/burst/maxburst\" specification\n");
     91 				return -1;
     92 			}
     93 			if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) {
     94 				explain1(parm_name, *argv);
     95 				return -1;
     96 			}
     97 			ok++;
     98 		} else if (strcmp(*argv, "mtu") == 0 ||
     99 			   strcmp(*argv, "minburst") == 0) {
    100 			const char *parm_name = *argv;
    101 
    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 
    207 		if (prate64) {
    208 			double lim2 = prate64*(double)latency/TIME_UNITS_PER_SEC + mtu;
    209 
    210 			if (lim2 < lim)
    211 				lim = lim2;
    212 		}
    213 		opt.limit = lim;
    214 	}
    215 
    216 	opt.rate.mpu      = mpu;
    217 	opt.rate.overhead = overhead;
    218 	if (tc_calc_rtable(&opt.rate, rtab, Rcell_log, mtu, linklayer) < 0) {
    219 		fprintf(stderr, "tbf: failed to calculate rate table.\n");
    220 		return -1;
    221 	}
    222 	opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer);
    223 
    224 	if (opt.peakrate.rate) {
    225 		opt.peakrate.mpu      = mpu;
    226 		opt.peakrate.overhead = overhead;
    227 		if (tc_calc_rtable(&opt.peakrate, ptab, Pcell_log, mtu, linklayer) < 0) {
    228 			fprintf(stderr, "tbf: failed to calculate peak rate table.\n");
    229 			return -1;
    230 		}
    231 		opt.mtu = tc_calc_xmittime(opt.peakrate.rate, mtu);
    232 	}
    233 
    234 	tail = NLMSG_TAIL(n);
    235 	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
    236 	addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt));
    237 	addattr_l(n, 2124, TCA_TBF_BURST, &buffer, sizeof(buffer));
    238 	if (rate64 >= (1ULL << 32))
    239 		addattr_l(n, 2124, TCA_TBF_RATE64, &rate64, sizeof(rate64));
    240 	addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024);
    241 	if (opt.peakrate.rate) {
    242 		if (prate64 >= (1ULL << 32))
    243 			addattr_l(n, 3124, TCA_TBF_PRATE64, &prate64, sizeof(prate64));
    244 		addattr_l(n, 3224, TCA_TBF_PBURST, &mtu, sizeof(mtu));
    245 		addattr_l(n, 4096, TCA_TBF_PTAB, ptab, 1024);
    246 	}
    247 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
    248 	return 0;
    249 }
    250 
    251 static int tbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
    252 {
    253 	struct rtattr *tb[TCA_TBF_MAX+1];
    254 	struct tc_tbf_qopt *qopt;
    255 	unsigned int linklayer;
    256 	double buffer, mtu;
    257 	double latency;
    258 	__u64 rate64 = 0, prate64 = 0;
    259 
    260 	SPRINT_BUF(b1);
    261 	SPRINT_BUF(b2);
    262 	SPRINT_BUF(b3);
    263 
    264 	if (opt == NULL)
    265 		return 0;
    266 
    267 	parse_rtattr_nested(tb, TCA_TBF_MAX, opt);
    268 
    269 	if (tb[TCA_TBF_PARMS] == NULL)
    270 		return -1;
    271 
    272 	qopt = RTA_DATA(tb[TCA_TBF_PARMS]);
    273 	if (RTA_PAYLOAD(tb[TCA_TBF_PARMS])  < sizeof(*qopt))
    274 		return -1;
    275 	rate64 = qopt->rate.rate;
    276 	if (tb[TCA_TBF_RATE64] &&
    277 	    RTA_PAYLOAD(tb[TCA_TBF_RATE64]) >= sizeof(rate64))
    278 		rate64 = rta_getattr_u64(tb[TCA_TBF_RATE64]);
    279 	fprintf(f, "rate %s ", sprint_rate(rate64, b1));
    280 	buffer = tc_calc_xmitsize(rate64, qopt->buffer);
    281 	if (show_details) {
    282 		fprintf(f, "burst %s/%u mpu %s ", sprint_size(buffer, b1),
    283 			1<<qopt->rate.cell_log, sprint_size(qopt->rate.mpu, b2));
    284 	} else {
    285 		fprintf(f, "burst %s ", sprint_size(buffer, b1));
    286 	}
    287 	if (show_raw)
    288 		fprintf(f, "[%08x] ", qopt->buffer);
    289 	prate64 = qopt->peakrate.rate;
    290 	if (tb[TCA_TBF_PRATE64] &&
    291 	    RTA_PAYLOAD(tb[TCA_TBF_PRATE64]) >= sizeof(prate64))
    292 		prate64 = rta_getattr_u64(tb[TCA_TBF_PRATE64]);
    293 	if (prate64) {
    294 		fprintf(f, "peakrate %s ", sprint_rate(prate64, b1));
    295 		if (qopt->mtu || qopt->peakrate.mpu) {
    296 			mtu = tc_calc_xmitsize(prate64, qopt->mtu);
    297 			if (show_details) {
    298 				fprintf(f, "mtu %s/%u mpu %s ", sprint_size(mtu, b1),
    299 					1<<qopt->peakrate.cell_log, sprint_size(qopt->peakrate.mpu, b2));
    300 			} else {
    301 				fprintf(f, "minburst %s ", sprint_size(mtu, b1));
    302 			}
    303 			if (show_raw)
    304 				fprintf(f, "[%08x] ", qopt->mtu);
    305 		}
    306 	}
    307 
    308 	latency = TIME_UNITS_PER_SEC*(qopt->limit/(double)rate64) - tc_core_tick2time(qopt->buffer);
    309 	if (prate64) {
    310 		double lat2 = TIME_UNITS_PER_SEC*(qopt->limit/(double)prate64) - tc_core_tick2time(qopt->mtu);
    311 
    312 		if (lat2 > latency)
    313 			latency = lat2;
    314 	}
    315 	if (latency >= 0.0)
    316 		fprintf(f, "lat %s ", sprint_time(latency, b1));
    317 	if (show_raw || latency < 0.0)
    318 		fprintf(f, "limit %s ", sprint_size(qopt->limit, b1));
    319 
    320 	if (qopt->rate.overhead) {
    321 		fprintf(f, "overhead %d", qopt->rate.overhead);
    322 	}
    323 	linklayer = (qopt->rate.linklayer & TC_LINKLAYER_MASK);
    324 	if (linklayer > TC_LINKLAYER_ETHERNET || show_details)
    325 		fprintf(f, "linklayer %s ", sprint_linklayer(linklayer, b3));
    326 
    327 	return 0;
    328 }
    329 
    330 struct qdisc_util tbf_qdisc_util = {
    331 	.id		= "tbf",
    332 	.parse_qopt	= tbf_parse_opt,
    333 	.print_qopt	= tbf_print_opt,
    334 };
    335