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 ] "); 30 fprintf(stderr, "[ overhead BYTES ] [ linklayer TYPE ]\n"); 31} 32 33static void explain1(char *arg) 34{ 35 fprintf(stderr, "Illegal \"%s\"\n", arg); 36} 37 38 39static 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 51 memset(&opt, 0, sizeof(opt)); 52 53 while (argc > 0) { 54 if (matches(*argv, "limit") == 0) { 55 NEXT_ARG(); 56 if (opt.limit || latency) { 57 fprintf(stderr, "Double \"limit/latency\" spec\n"); 58 return -1; 59 } 60 if (get_size(&opt.limit, *argv)) { 61 explain1("limit"); 62 return -1; 63 } 64 ok++; 65 } else if (matches(*argv, "latency") == 0) { 66 NEXT_ARG(); 67 if (opt.limit || latency) { 68 fprintf(stderr, "Double \"limit/latency\" spec\n"); 69 return -1; 70 } 71 if (get_time(&latency, *argv)) { 72 explain1("latency"); 73 return -1; 74 } 75 ok++; 76 } else if (matches(*argv, "burst") == 0 || 77 strcmp(*argv, "buffer") == 0 || 78 strcmp(*argv, "maxburst") == 0) { 79 NEXT_ARG(); 80 if (buffer) { 81 fprintf(stderr, "Double \"buffer/burst\" spec\n"); 82 return -1; 83 } 84 if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) { 85 explain1("buffer"); 86 return -1; 87 } 88 ok++; 89 } else if (strcmp(*argv, "mtu") == 0 || 90 strcmp(*argv, "minburst") == 0) { 91 NEXT_ARG(); 92 if (mtu) { 93 fprintf(stderr, "Double \"mtu/minburst\" spec\n"); 94 return -1; 95 } 96 if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) { 97 explain1("mtu"); 98 return -1; 99 } 100 ok++; 101 } else if (strcmp(*argv, "mpu") == 0) { 102 NEXT_ARG(); 103 if (mpu) { 104 fprintf(stderr, "Double \"mpu\" spec\n"); 105 return -1; 106 } 107 if (get_size(&mpu, *argv)) { 108 explain1("mpu"); 109 return -1; 110 } 111 ok++; 112 } else if (strcmp(*argv, "rate") == 0) { 113 NEXT_ARG(); 114 if (opt.rate.rate) { 115 fprintf(stderr, "Double \"rate\" spec\n"); 116 return -1; 117 } 118 if (get_rate(&opt.rate.rate, *argv)) { 119 explain1("rate"); 120 return -1; 121 } 122 ok++; 123 } else if (matches(*argv, "peakrate") == 0) { 124 NEXT_ARG(); 125 if (opt.peakrate.rate) { 126 fprintf(stderr, "Double \"peakrate\" spec\n"); 127 return -1; 128 } 129 if (get_rate(&opt.peakrate.rate, *argv)) { 130 explain1("peakrate"); 131 return -1; 132 } 133 ok++; 134 } else if (matches(*argv, "overhead") == 0) { 135 NEXT_ARG(); 136 if (overhead) { 137 fprintf(stderr, "Double \"overhead\" spec\n"); 138 return -1; 139 } 140 if (get_u16(&overhead, *argv, 10)) { 141 explain1("overhead"); return -1; 142 } 143 } else if (matches(*argv, "linklayer") == 0) { 144 NEXT_ARG(); 145 if (get_linklayer(&linklayer, *argv)) { 146 explain1("linklayer"); return -1; 147 } 148 } else if (strcmp(*argv, "help") == 0) { 149 explain(); 150 return -1; 151 } else { 152 fprintf(stderr, "What is \"%s\"?\n", *argv); 153 explain(); 154 return -1; 155 } 156 argc--; argv++; 157 } 158 159 if (!ok) { 160 explain(); 161 return -1; 162 } 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 218static 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 281struct qdisc_util tbf_qdisc_util = { 282 .id = "tbf", 283 .parse_qopt = tbf_parse_opt, 284 .print_qopt = tbf_print_opt, 285}; 286 287