1 /* 2 * q_gred.c GRED. 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: J Hadi Salim(hadi (at) nortelnetworks.com) 10 * code ruthlessly ripped from 11 * Alexey Kuznetsov, <kuznet (at) ms2.inr.ac.ru> 12 * 13 */ 14 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <unistd.h> 18 #include <syslog.h> 19 #include <fcntl.h> 20 #include <sys/socket.h> 21 #include <netinet/in.h> 22 #include <arpa/inet.h> 23 #include <string.h> 24 #include <math.h> 25 26 #include "utils.h" 27 #include "tc_util.h" 28 29 #include "tc_red.h" 30 31 32 #if 0 33 #define DPRINTF(format,args...) fprintf(stderr,format,##args) 34 #else 35 #define DPRINTF(format,args...) 36 #endif 37 38 static void explain(void) 39 { 40 fprintf(stderr, "Usage: tc qdisc { add | replace | change } ... gred setup vqs NUMBER\n"); 41 fprintf(stderr, " default DEFAULT_VQ [ grio ] [ limit BYTES ]\n"); 42 fprintf(stderr, " tc qdisc change ... gred vq VQ [ prio VALUE ] limit BYTES\n"); 43 fprintf(stderr, " min BYTES max BYTES avpkt BYTES [ burst PACKETS ]\n"); 44 fprintf(stderr, " [ probability PROBABILITY ] [ bandwidth KBPS ]\n"); 45 } 46 47 static int init_gred(struct qdisc_util *qu, int argc, char **argv, 48 struct nlmsghdr *n) 49 { 50 51 struct rtattr *tail; 52 struct tc_gred_sopt opt = { 0 }; 53 __u32 limit = 0; 54 55 opt.def_DP = MAX_DPs; 56 57 while (argc > 0) { 58 DPRINTF(stderr,"init_gred: invoked with %s\n",*argv); 59 if (strcmp(*argv, "vqs") == 0 || 60 strcmp(*argv, "DPs") == 0) { 61 NEXT_ARG(); 62 if (get_unsigned(&opt.DPs, *argv, 10)) { 63 fprintf(stderr, "Illegal \"vqs\"\n"); 64 return -1; 65 } else if (opt.DPs > MAX_DPs) { 66 fprintf(stderr, "GRED: only %u VQs are " 67 "currently supported\n", MAX_DPs); 68 return -1; 69 } 70 } else if (strcmp(*argv, "default") == 0) { 71 if (opt.DPs == 0) { 72 fprintf(stderr, "\"default\" must be defined " 73 "after \"vqs\"\n"); 74 return -1; 75 } 76 NEXT_ARG(); 77 if (get_unsigned(&opt.def_DP, *argv, 10)) { 78 fprintf(stderr, "Illegal \"default\"\n"); 79 return -1; 80 } else if (opt.def_DP >= opt.DPs) { 81 fprintf(stderr, "\"default\" must be less than " 82 "\"vqs\"\n"); 83 return -1; 84 } 85 } else if (strcmp(*argv, "grio") == 0) { 86 opt.grio = 1; 87 } else if (strcmp(*argv, "limit") == 0) { 88 NEXT_ARG(); 89 if (get_size(&limit, *argv)) { 90 fprintf(stderr, "Illegal \"limit\"\n"); 91 return -1; 92 } 93 } else if (strcmp(*argv, "help") == 0) { 94 explain(); 95 return -1; 96 } else { 97 fprintf(stderr, "What is \"%s\"?\n", *argv); 98 explain(); 99 return -1; 100 } 101 argc--; argv++; 102 } 103 104 if (!opt.DPs || opt.def_DP == MAX_DPs) { 105 fprintf(stderr, "Illegal gred setup parameters \n"); 106 return -1; 107 } 108 109 DPRINTF("TC_GRED: sending DPs=%u def_DP=%u\n",opt.DPs,opt.def_DP); 110 n->nlmsg_flags|=NLM_F_CREATE; 111 tail = NLMSG_TAIL(n); 112 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); 113 addattr_l(n, 1024, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt)); 114 if (limit) 115 addattr32(n, 1024, TCA_GRED_LIMIT, limit); 116 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 117 return 0; 118 } 119 /* 120 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 121 */ 122 static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) 123 { 124 int ok=0; 125 struct tc_gred_qopt opt = { 0 }; 126 unsigned burst = 0; 127 unsigned avpkt = 0; 128 double probability = 0.02; 129 unsigned rate = 0; 130 int parm; 131 __u8 sbuf[256]; 132 struct rtattr *tail; 133 __u32 max_P; 134 135 opt.DP = MAX_DPs; 136 137 while (argc > 0) { 138 if (strcmp(*argv, "limit") == 0) { 139 NEXT_ARG(); 140 if (get_size(&opt.limit, *argv)) { 141 fprintf(stderr, "Illegal \"limit\"\n"); 142 return -1; 143 } 144 ok++; 145 } else if (strcmp(*argv, "setup") == 0) { 146 if (ok) { 147 fprintf(stderr, "Illegal \"setup\"\n"); 148 return -1; 149 } 150 return init_gred(qu, argc-1, argv+1, n); 151 } else if (strcmp(*argv, "min") == 0) { 152 NEXT_ARG(); 153 if (get_size(&opt.qth_min, *argv)) { 154 fprintf(stderr, "Illegal \"min\"\n"); 155 return -1; 156 } 157 ok++; 158 } else if (strcmp(*argv, "max") == 0) { 159 NEXT_ARG(); 160 if (get_size(&opt.qth_max, *argv)) { 161 fprintf(stderr, "Illegal \"max\"\n"); 162 return -1; 163 } 164 ok++; 165 } else if (strcmp(*argv, "vq") == 0 || 166 strcmp(*argv, "DP") == 0) { 167 NEXT_ARG(); 168 if (get_unsigned(&opt.DP, *argv, 10)) { 169 fprintf(stderr, "Illegal \"vq\"\n"); 170 return -1; 171 } else if (opt.DP >= MAX_DPs) { 172 fprintf(stderr, "GRED: only %u VQs are " 173 "currently supported\n", MAX_DPs); 174 return -1; 175 } /* need a better error check */ 176 ok++; 177 } else if (strcmp(*argv, "burst") == 0) { 178 NEXT_ARG(); 179 if (get_unsigned(&burst, *argv, 0)) { 180 fprintf(stderr, "Illegal \"burst\"\n"); 181 return -1; 182 } 183 ok++; 184 } else if (strcmp(*argv, "avpkt") == 0) { 185 NEXT_ARG(); 186 if (get_size(&avpkt, *argv)) { 187 fprintf(stderr, "Illegal \"avpkt\"\n"); 188 return -1; 189 } 190 ok++; 191 } else if (strcmp(*argv, "probability") == 0) { 192 NEXT_ARG(); 193 if (sscanf(*argv, "%lg", &probability) != 1) { 194 fprintf(stderr, "Illegal \"probability\"\n"); 195 return -1; 196 } 197 ok++; 198 } else if (strcmp(*argv, "prio") == 0) { 199 NEXT_ARG(); 200 opt.prio=strtol(*argv, (char **)NULL, 10); 201 /* some error check here */ 202 ok++; 203 } else if (strcmp(*argv, "bandwidth") == 0) { 204 NEXT_ARG(); 205 if (get_rate(&rate, *argv)) { 206 fprintf(stderr, "Illegal \"bandwidth\"\n"); 207 return -1; 208 } 209 ok++; 210 } else if (strcmp(*argv, "help") == 0) { 211 explain(); 212 return -1; 213 } else { 214 fprintf(stderr, "What is \"%s\"?\n", *argv); 215 explain(); 216 return -1; 217 } 218 argc--; argv++; 219 } 220 221 if (!ok) { 222 explain(); 223 return -1; 224 } 225 if (opt.DP == MAX_DPs || !opt.limit || !opt.qth_min || !opt.qth_max || 226 !avpkt) { 227 fprintf(stderr, "Required parameter (vq, limit, min, max, " 228 "avpkt) is missing\n"); 229 return -1; 230 } 231 if (!burst) { 232 burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt); 233 fprintf(stderr, "GRED: set burst to %u\n", burst); 234 } 235 if (!rate) { 236 get_rate(&rate, "10Mbit"); 237 fprintf(stderr, "GRED: set bandwidth to 10Mbit\n"); 238 } 239 if ((parm = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) { 240 fprintf(stderr, "GRED: failed to calculate EWMA constant.\n"); 241 return -1; 242 } 243 if (parm >= 10) 244 fprintf(stderr, "GRED: WARNING. Burst %u seems to be too " 245 "large.\n", burst); 246 opt.Wlog = parm; 247 if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) { 248 fprintf(stderr, "GRED: failed to calculate probability.\n"); 249 return -1; 250 } 251 opt.Plog = parm; 252 if ((parm = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) 253 { 254 fprintf(stderr, "GRED: failed to calculate idle damping " 255 "table.\n"); 256 return -1; 257 } 258 opt.Scell_log = parm; 259 260 tail = NLMSG_TAIL(n); 261 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); 262 addattr_l(n, 1024, TCA_GRED_PARMS, &opt, sizeof(opt)); 263 addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256); 264 max_P = probability * pow(2, 32); 265 addattr32(n, 1024, TCA_GRED_MAX_P, max_P); 266 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 267 return 0; 268 } 269 270 static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) 271 { 272 struct rtattr *tb[TCA_GRED_MAX + 1]; 273 struct tc_gred_sopt *sopt; 274 struct tc_gred_qopt *qopt; 275 __u32 *max_p = NULL; 276 __u32 *limit = NULL; 277 unsigned i; 278 SPRINT_BUF(b1); 279 SPRINT_BUF(b2); 280 SPRINT_BUF(b3); 281 282 if (opt == NULL) 283 return 0; 284 285 parse_rtattr_nested(tb, TCA_GRED_MAX, opt); 286 287 if (tb[TCA_GRED_PARMS] == NULL) 288 return -1; 289 290 if (tb[TCA_GRED_MAX_P] && 291 RTA_PAYLOAD(tb[TCA_GRED_MAX_P]) >= sizeof(__u32) * MAX_DPs) 292 max_p = RTA_DATA(tb[TCA_GRED_MAX_P]); 293 294 if (tb[TCA_GRED_LIMIT] && 295 RTA_PAYLOAD(tb[TCA_GRED_LIMIT]) == sizeof(__u32)) 296 limit = RTA_DATA(tb[TCA_GRED_LIMIT]); 297 298 sopt = RTA_DATA(tb[TCA_GRED_DPS]); 299 qopt = RTA_DATA(tb[TCA_GRED_PARMS]); 300 if (RTA_PAYLOAD(tb[TCA_GRED_DPS]) < sizeof(*sopt) || 301 RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) { 302 fprintf(f,"\n GRED received message smaller than expected\n"); 303 return -1; 304 } 305 306 /* Bad hack! should really return a proper message as shown above*/ 307 308 fprintf(f, "vqs %u default %u %s", 309 sopt->DPs, 310 sopt->def_DP, 311 sopt->grio ? "grio " : ""); 312 313 if (limit) 314 fprintf(f, "limit %s ", 315 sprint_size(*limit, b1)); 316 317 for (i=0;i<MAX_DPs;i++, qopt++) { 318 if (qopt->DP >= MAX_DPs) continue; 319 fprintf(f, "\n vq %u prio %hhu limit %s min %s max %s ", 320 qopt->DP, 321 qopt->prio, 322 sprint_size(qopt->limit, b1), 323 sprint_size(qopt->qth_min, b2), 324 sprint_size(qopt->qth_max, b3)); 325 if (show_details) { 326 fprintf(f, "ewma %u ", qopt->Wlog); 327 if (max_p) 328 fprintf(f, "probability %lg ", max_p[i] / pow(2, 32)); 329 else 330 fprintf(f, "Plog %u ", qopt->Plog); 331 fprintf(f, "Scell_log %u ", qopt->Scell_log); 332 } 333 if (show_stats) { 334 fprintf(f, "\n Queue size: average %s current %s ", 335 sprint_size(qopt->qave, b1), 336 sprint_size(qopt->backlog, b2)); 337 fprintf(f, "\n Dropped packets: forced %u early %u pdrop %u other %u ", 338 qopt->forced, 339 qopt->early, 340 qopt->pdrop, 341 qopt->other); 342 fprintf(f, "\n Total packets: %u (%s) ", 343 qopt->packets, 344 sprint_size(qopt->bytesin, b1)); 345 } 346 } 347 return 0; 348 } 349 350 struct qdisc_util gred_qdisc_util = { 351 .id = "gred", 352 .parse_qopt = gred_parse_opt, 353 .print_qopt = gred_print_opt, 354 }; 355