q_tbf.c revision 7b77c0caa6479aaf8e1e473a24372965f3315190
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@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 26static 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 ]\n"); 30} 31 32static void explain1(char *arg) 33{ 34 fprintf(stderr, "Illegal \"%s\"\n", arg); 35} 36 37 38#define usage() return(-1) 39 40static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) 41{ 42 int ok=0; 43 struct tc_tbf_qopt opt; 44 __u32 rtab[256]; 45 __u32 ptab[256]; 46 unsigned buffer=0, mtu=0, mpu=0, latency=0; 47 int Rcell_log=-1, Pcell_log = -1; 48 struct rtattr *tail; 49 50 memset(&opt, 0, sizeof(opt)); 51 52 while (argc > 0) { 53 if (matches(*argv, "limit") == 0) { 54 NEXT_ARG(); 55 if (opt.limit || latency) { 56 fprintf(stderr, "Double \"limit/latency\" spec\n"); 57 return -1; 58 } 59 if (get_size(&opt.limit, *argv)) { 60 explain1("limit"); 61 return -1; 62 } 63 ok++; 64 } else if (matches(*argv, "latency") == 0) { 65 NEXT_ARG(); 66 if (opt.limit || latency) { 67 fprintf(stderr, "Double \"limit/latency\" spec\n"); 68 return -1; 69 } 70 if (get_usecs(&latency, *argv)) { 71 explain1("latency"); 72 return -1; 73 } 74 ok++; 75 } else if (matches(*argv, "burst") == 0 || 76 strcmp(*argv, "buffer") == 0 || 77 strcmp(*argv, "maxburst") == 0) { 78 NEXT_ARG(); 79 if (buffer) { 80 fprintf(stderr, "Double \"buffer/burst\" spec\n"); 81 return -1; 82 } 83 if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) { 84 explain1("buffer"); 85 return -1; 86 } 87 ok++; 88 } else if (strcmp(*argv, "mtu") == 0 || 89 strcmp(*argv, "minburst") == 0) { 90 NEXT_ARG(); 91 if (mtu) { 92 fprintf(stderr, "Double \"mtu/minburst\" spec\n"); 93 return -1; 94 } 95 if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) { 96 explain1("mtu"); 97 return -1; 98 } 99 ok++; 100 } else if (strcmp(*argv, "mpu") == 0) { 101 NEXT_ARG(); 102 if (mpu) { 103 fprintf(stderr, "Double \"mpu\" spec\n"); 104 return -1; 105 } 106 if (get_size(&mpu, *argv)) { 107 explain1("mpu"); 108 return -1; 109 } 110 ok++; 111 } else if (strcmp(*argv, "rate") == 0) { 112 NEXT_ARG(); 113 if (opt.rate.rate) { 114 fprintf(stderr, "Double \"rate\" spec\n"); 115 return -1; 116 } 117 if (get_rate(&opt.rate.rate, *argv)) { 118 explain1("rate"); 119 return -1; 120 } 121 ok++; 122 } else if (matches(*argv, "peakrate") == 0) { 123 NEXT_ARG(); 124 if (opt.peakrate.rate) { 125 fprintf(stderr, "Double \"peakrate\" spec\n"); 126 return -1; 127 } 128 if (get_rate(&opt.peakrate.rate, *argv)) { 129 explain1("peakrate"); 130 return -1; 131 } 132 ok++; 133 } else if (strcmp(*argv, "help") == 0) { 134 explain(); 135 return -1; 136 } else { 137 fprintf(stderr, "What is \"%s\"?\n", *argv); 138 explain(); 139 return -1; 140 } 141 argc--; argv++; 142 } 143 144 if (!ok) 145 return 0; 146 147 if (opt.rate.rate == 0 || !buffer) { 148 fprintf(stderr, "Both \"rate\" and \"burst\" are required.\n"); 149 return -1; 150 } 151 if (opt.peakrate.rate) { 152 if (!mtu) { 153 fprintf(stderr, "\"mtu\" is required, if \"peakrate\" is requested.\n"); 154 return -1; 155 } 156 } 157 158 if (opt.limit == 0 && latency == 0) { 159 fprintf(stderr, "Either \"limit\" or \"latency\" are required.\n"); 160 return -1; 161 } 162 163 if (opt.limit == 0) { 164 double lim = opt.rate.rate*(double)latency/1000000 + buffer; 165 if (opt.peakrate.rate) { 166 double lim2 = opt.peakrate.rate*(double)latency/1000000 + mtu; 167 if (lim2 < lim) 168 lim = lim2; 169 } 170 opt.limit = lim; 171 } 172 173 if ((Rcell_log = tc_calc_rtable(opt.rate.rate, rtab, Rcell_log, mtu, mpu)) < 0) { 174 fprintf(stderr, "TBF: failed to calculate rate table.\n"); 175 return -1; 176 } 177 opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer); 178 opt.rate.cell_log = Rcell_log; 179 opt.rate.mpu = mpu; 180 if (opt.peakrate.rate) { 181 if ((Pcell_log = tc_calc_rtable(opt.peakrate.rate, ptab, Pcell_log, mtu, mpu)) < 0) { 182 fprintf(stderr, "TBF: failed to calculate peak rate table.\n"); 183 return -1; 184 } 185 opt.mtu = tc_calc_xmittime(opt.peakrate.rate, mtu); 186 opt.peakrate.cell_log = Pcell_log; 187 opt.peakrate.mpu = mpu; 188 } 189 190 tail = NLMSG_TAIL(n); 191 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); 192 addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt)); 193 addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024); 194 if (opt.peakrate.rate) 195 addattr_l(n, 4096, TCA_TBF_PTAB, ptab, 1024); 196 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 197 return 0; 198} 199 200static int tbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) 201{ 202 struct rtattr *tb[TCA_TBF_PTAB+1]; 203 struct tc_tbf_qopt *qopt; 204 double buffer, mtu; 205 double latency; 206 SPRINT_BUF(b1); 207 SPRINT_BUF(b2); 208 209 if (opt == NULL) 210 return 0; 211 212 parse_rtattr_nested(tb, TCA_TBF_PTAB, opt); 213 214 if (tb[TCA_TBF_PARMS] == NULL) 215 return -1; 216 217 qopt = RTA_DATA(tb[TCA_TBF_PARMS]); 218 if (RTA_PAYLOAD(tb[TCA_TBF_PARMS]) < sizeof(*qopt)) 219 return -1; 220 fprintf(f, "rate %s ", sprint_rate(qopt->rate.rate, b1)); 221 buffer = ((double)qopt->rate.rate*tc_core_tick2usec(qopt->buffer))/1000000; 222 if (show_details) { 223 fprintf(f, "burst %s/%u mpu %s ", sprint_size(buffer, b1), 224 1<<qopt->rate.cell_log, sprint_size(qopt->rate.mpu, b2)); 225 } else { 226 fprintf(f, "burst %s ", sprint_size(buffer, b1)); 227 } 228 if (show_raw) 229 fprintf(f, "[%08x] ", qopt->buffer); 230 if (qopt->peakrate.rate) { 231 fprintf(f, "peakrate %s ", sprint_rate(qopt->peakrate.rate, b1)); 232 if (qopt->mtu || qopt->peakrate.mpu) { 233 mtu = ((double)qopt->peakrate.rate*tc_core_tick2usec(qopt->mtu))/1000000; 234 if (show_details) { 235 fprintf(f, "mtu %s/%u mpu %s ", sprint_size(mtu, b1), 236 1<<qopt->peakrate.cell_log, sprint_size(qopt->peakrate.mpu, b2)); 237 } else { 238 fprintf(f, "minburst %s ", sprint_size(mtu, b1)); 239 } 240 if (show_raw) 241 fprintf(f, "[%08x] ", qopt->mtu); 242 } 243 } 244 245 if (show_raw) 246 fprintf(f, "limit %s ", sprint_size(qopt->limit, b1)); 247 248 latency = 1000000*(qopt->limit/(double)qopt->rate.rate) - tc_core_tick2usec(qopt->buffer); 249 if (qopt->peakrate.rate) { 250 double lat2 = 1000000*(qopt->limit/(double)qopt->peakrate.rate) - tc_core_tick2usec(qopt->mtu); 251 if (lat2 > latency) 252 latency = lat2; 253 } 254 fprintf(f, "lat %s ", sprint_usecs(latency, b1)); 255 256 return 0; 257} 258 259struct qdisc_util tbf_qdisc_util = { 260 .id = "tbf", 261 .parse_qopt = tbf_parse_opt, 262 .print_qopt = tbf_print_opt, 263}; 264 265