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