1 /* $KAME: qop_priq.c,v 1.1 2000/10/18 09:15:19 kjc Exp $ */ 2 /* 3 * Copyright (C) 2000 4 * Sony Computer Science Laboratories, Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/socket.h> 30 #include <sys/sockio.h> 31 #include <sys/ioctl.h> 32 #include <sys/fcntl.h> 33 #include <net/if.h> 34 #include <netinet/in.h> 35 #include <arpa/inet.h> 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 #include <stddef.h> 41 #include <string.h> 42 #include <ctype.h> 43 #include <errno.h> 44 #include <syslog.h> 45 #include <netdb.h> 46 47 #include <altq/altq.h> 48 #include <altq/altq_priq.h> 49 #include "altq_qop.h" 50 #include "qop_priq.h" 51 52 static int qop_priq_enable_hook(struct ifinfo *ifinfo); 53 54 static int priq_attach(struct ifinfo *ifinfo); 55 static int priq_detach(struct ifinfo *ifinfo); 56 static int priq_clear(struct ifinfo *ifinfo); 57 static int priq_enable(struct ifinfo *ifinfo); 58 static int priq_disable(struct ifinfo *ifinfo); 59 static int priq_add_class(struct classinfo *clinfo); 60 static int priq_modify_class(struct classinfo *clinfo, void *arg); 61 static int priq_delete_class(struct classinfo *clinfo); 62 static int priq_add_filter(struct fltrinfo *fltrinfo); 63 static int priq_delete_filter(struct fltrinfo *fltrinfo); 64 65 #define PRIQ_DEVICE "/dev/altq/priq" 66 67 static int priq_fd = -1; 68 static int priq_refcount = 0; 69 70 static struct qdisc_ops priq_qdisc = { 71 ALTQT_PRIQ, 72 "priq", 73 priq_attach, 74 priq_detach, 75 priq_clear, 76 priq_enable, 77 priq_disable, 78 priq_add_class, 79 priq_modify_class, 80 priq_delete_class, 81 priq_add_filter, 82 priq_delete_filter, 83 }; 84 85 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) 86 87 /* 88 * parser interface 89 */ 90 int 91 priq_interface_parser(const char *ifname, int argc, char **argv) 92 { 93 u_int bandwidth = 100000000; /* 100Mbps */ 94 u_int tbrsize = 0; 95 int flags = 0; 96 97 /* 98 * process options 99 */ 100 while (argc > 0) { 101 if (EQUAL(*argv, "bandwidth")) { 102 argc--; argv++; 103 if (argc > 0) 104 bandwidth = atobps(*argv); 105 } else if (EQUAL(*argv, "tbrsize")) { 106 argc--; argv++; 107 if (argc > 0) 108 tbrsize = atobytes(*argv); 109 } else if (EQUAL(*argv, "priq")) { 110 /* just skip */ 111 } else { 112 LOG(LOG_ERR, 0, "Unknown keyword '%s'\n", argv); 113 return (0); 114 } 115 argc--; argv++; 116 } 117 118 if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0) 119 return (0); 120 121 if (qcmd_priq_add_if(ifname, bandwidth, flags) != 0) 122 return (0); 123 return (1); 124 } 125 126 int 127 priq_class_parser(const char *ifname, const char *class_name, 128 const char *parent_name, int argc, char **argv) 129 { 130 int pri = 0, qlimit = 50; 131 int flags = 0, error; 132 133 while (argc > 0) { 134 if (EQUAL(*argv, "priority")) { 135 argc--; argv++; 136 if (argc > 0) 137 pri = strtoul(*argv, NULL, 0); 138 } else if (EQUAL(*argv, "qlimit")) { 139 argc--; argv++; 140 if (argc > 0) 141 qlimit = strtoul(*argv, NULL, 0); 142 } else if (EQUAL(*argv, "default")) { 143 flags |= PRCF_DEFAULTCLASS; 144 } else if (EQUAL(*argv, "red")) { 145 flags |= PRCF_RED; 146 } else if (EQUAL(*argv, "ecn")) { 147 flags |= PRCF_ECN; 148 } else if (EQUAL(*argv, "rio")) { 149 flags |= PRCF_RIO; 150 } else if (EQUAL(*argv, "cleardscp")) { 151 flags |= PRCF_CLEARDSCP; 152 } else { 153 LOG(LOG_ERR, 0, 154 "Unknown keyword '%s' in %s, line %d\n", 155 *argv, altqconfigfile, line_no); 156 return (0); 157 } 158 159 argc--; argv++; 160 } 161 162 if ((flags & PRCF_ECN) && (flags & (PRCF_RED|PRCF_RIO)) == 0) 163 flags |= PRCF_RED; 164 165 error = qcmd_priq_add_class(ifname, class_name, pri, qlimit, flags); 166 167 if (error) { 168 LOG(LOG_ERR, errno, "priq_class_parser: %s\n", 169 qoperror(error)); 170 return (0); 171 } 172 return (1); 173 } 174 175 /* 176 * qcmd api 177 */ 178 int 179 qcmd_priq_add_if(const char *ifname, u_int bandwidth, int flags) 180 { 181 int error; 182 183 error = qop_priq_add_if(NULL, ifname, bandwidth, flags); 184 if (error != 0) 185 LOG(LOG_ERR, errno, "%s: can't add priq on interface '%s'\n", 186 qoperror(error), ifname); 187 return (error); 188 } 189 190 int 191 qcmd_priq_add_class(const char *ifname, const char *class_name, 192 int pri, int qlimit, int flags) 193 { 194 struct ifinfo *ifinfo; 195 int error = 0; 196 197 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 198 error = QOPERR_BADIF; 199 200 if (error == 0) 201 error = qop_priq_add_class(NULL, class_name, ifinfo, 202 pri, qlimit, flags); 203 if (error != 0) 204 LOG(LOG_ERR, errno, 205 "priq: %s: can't add class '%s' on interface '%s'\n", 206 qoperror(error), class_name, ifname); 207 return (error); 208 } 209 210 int 211 qcmd_priq_modify_class(const char *ifname, const char *class_name, 212 int pri, int qlimit, int flags) 213 { 214 struct ifinfo *ifinfo; 215 struct classinfo *clinfo; 216 217 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 218 return (QOPERR_BADIF); 219 220 if ((clinfo = clname2clinfo(ifinfo, class_name)) == NULL) 221 return (QOPERR_BADCLASS); 222 223 return qop_priq_modify_class(clinfo, pri, qlimit, flags); 224 } 225 226 /* 227 * qop api 228 */ 229 int 230 qop_priq_add_if(struct ifinfo **rp, const char *ifname, 231 u_int bandwidth, int flags) 232 { 233 struct ifinfo *ifinfo = NULL; 234 struct priq_ifinfo *priq_ifinfo = NULL; 235 int error; 236 237 if ((priq_ifinfo = calloc(1, sizeof(*priq_ifinfo))) == NULL) 238 return (QOPERR_NOMEM); 239 240 error = qop_add_if(&ifinfo, ifname, bandwidth, 241 &priq_qdisc, priq_ifinfo); 242 if (error != 0) 243 goto err_ret; 244 245 /* set enable hook */ 246 ifinfo->enable_hook = qop_priq_enable_hook; 247 248 if (rp != NULL) 249 *rp = ifinfo; 250 return (0); 251 252 err_ret: 253 if (priq_ifinfo != NULL) { 254 free(priq_ifinfo); 255 if (ifinfo != NULL) 256 ifinfo->private = NULL; 257 } 258 return (error); 259 } 260 261 int 262 qop_priq_add_class(struct classinfo **rp, const char *class_name, 263 struct ifinfo *ifinfo, int pri, int qlimit, int flags) 264 { 265 struct classinfo *clinfo; 266 struct priq_ifinfo *priq_ifinfo; 267 struct priq_classinfo *priq_clinfo = NULL; 268 int error; 269 270 priq_ifinfo = ifinfo->private; 271 if ((flags & PRCF_DEFAULTCLASS) && priq_ifinfo->default_class != NULL) 272 return (QOPERR_CLASS_INVAL); 273 274 if ((priq_clinfo = calloc(1, sizeof(*priq_clinfo))) == NULL) { 275 error = QOPERR_NOMEM; 276 goto err_ret; 277 } 278 279 priq_clinfo->pri = pri; 280 priq_clinfo->qlimit = qlimit; 281 priq_clinfo->flags = flags; 282 283 if ((error = qop_add_class(&clinfo, class_name, ifinfo, NULL, 284 priq_clinfo)) != 0) 285 goto err_ret; 286 287 if (flags & PRCF_DEFAULTCLASS) 288 priq_ifinfo->default_class = clinfo; 289 290 if (rp != NULL) 291 *rp = clinfo; 292 return (0); 293 294 err_ret: 295 if (priq_clinfo != NULL) { 296 free(priq_clinfo); 297 clinfo->private = NULL; 298 } 299 300 return (error); 301 } 302 303 int 304 qop_priq_modify_class(struct classinfo *clinfo, 305 int pri, int qlimit, int flags) 306 { 307 struct priq_classinfo *priq_clinfo, *parent_clinfo; 308 int error; 309 310 priq_clinfo = clinfo->private; 311 if (clinfo->parent == NULL) 312 return (QOPERR_CLASS_INVAL); 313 parent_clinfo = clinfo->parent->private; 314 315 priq_clinfo->pri = pri; 316 priq_clinfo->qlimit = qlimit; 317 priq_clinfo->flags = flags; 318 319 error = qop_modify_class(clinfo, NULL); 320 if (error == 0) 321 return (0); 322 return (error); 323 } 324 325 /* 326 * sanity check at enabling priq: 327 * 1. there must one default class for an interface 328 */ 329 static int 330 qop_priq_enable_hook(struct ifinfo *ifinfo) 331 { 332 struct priq_ifinfo *priq_ifinfo; 333 334 priq_ifinfo = ifinfo->private; 335 if (priq_ifinfo->default_class == NULL) { 336 LOG(LOG_ERR, 0, "priq: no default class on interface %s!\n", 337 ifinfo->ifname); 338 return (QOPERR_CLASS); 339 } 340 return (0); 341 } 342 343 /* 344 * system call interfaces for qdisc_ops 345 */ 346 static int 347 priq_attach(struct ifinfo *ifinfo) 348 { 349 struct priq_interface iface; 350 351 memset(&iface, 0, sizeof(iface)); 352 strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ); 353 354 if (priq_fd < 0 && 355 (priq_fd = open(PRIQ_DEVICE, O_RDWR)) < 0 && 356 (priq_fd = open_module(PRIQ_DEVICE, O_RDWR)) < 0) { 357 LOG(LOG_ERR, errno, "PRIQ open\n"); 358 return (QOPERR_SYSCALL); 359 } 360 361 priq_refcount++; 362 memset(&iface, 0, sizeof(iface)); 363 strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ); 364 iface.arg = ifinfo->bandwidth; 365 366 if (ioctl(priq_fd, PRIQ_IF_ATTACH, &iface) < 0) 367 return (QOPERR_SYSCALL); 368 return (0); 369 } 370 371 static int 372 priq_detach(struct ifinfo *ifinfo) 373 { 374 struct priq_interface iface; 375 376 memset(&iface, 0, sizeof(iface)); 377 strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ); 378 379 if (ioctl(priq_fd, PRIQ_IF_DETACH, &iface) < 0) 380 return (QOPERR_SYSCALL); 381 382 if (--priq_refcount == 0) { 383 close(priq_fd); 384 priq_fd = -1; 385 } 386 return (0); 387 } 388 389 static int 390 priq_clear(struct ifinfo *ifinfo) 391 { 392 struct priq_interface iface; 393 394 memset(&iface, 0, sizeof(iface)); 395 strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ); 396 397 if (ioctl(priq_fd, PRIQ_CLEAR, &iface) < 0) 398 return (QOPERR_SYSCALL); 399 return (0); 400 } 401 402 static int 403 priq_enable(struct ifinfo *ifinfo) 404 { 405 struct priq_interface iface; 406 407 memset(&iface, 0, sizeof(iface)); 408 strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ); 409 410 if (ioctl(priq_fd, PRIQ_ENABLE, &iface) < 0) 411 return (QOPERR_SYSCALL); 412 return (0); 413 } 414 415 static int 416 priq_disable(struct ifinfo *ifinfo) 417 { 418 struct priq_interface iface; 419 420 memset(&iface, 0, sizeof(iface)); 421 strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ); 422 423 if (ioctl(priq_fd, PRIQ_DISABLE, &iface) < 0) 424 return (QOPERR_SYSCALL); 425 return (0); 426 } 427 428 static int 429 priq_add_class(struct classinfo *clinfo) 430 { 431 struct priq_add_class class_add; 432 struct priq_classinfo *priq_clinfo; 433 struct priq_ifinfo *priq_ifinfo; 434 435 priq_ifinfo = clinfo->ifinfo->private; 436 priq_clinfo = clinfo->private; 437 438 memset(&class_add, 0, sizeof(class_add)); 439 strncpy(class_add.iface.ifname, clinfo->ifinfo->ifname, IFNAMSIZ); 440 441 class_add.pri = priq_clinfo->pri; 442 class_add.qlimit = priq_clinfo->qlimit; 443 class_add.flags = priq_clinfo->flags; 444 if (ioctl(priq_fd, PRIQ_ADD_CLASS, &class_add) < 0) { 445 clinfo->handle = PRIQ_NULLCLASS_HANDLE; 446 return (QOPERR_SYSCALL); 447 } 448 clinfo->handle = class_add.class_handle; 449 return (0); 450 } 451 452 static int 453 priq_modify_class(struct classinfo *clinfo, void *arg) 454 { 455 struct priq_modify_class class_mod; 456 struct priq_classinfo *priq_clinfo; 457 458 priq_clinfo = clinfo->private; 459 460 memset(&class_mod, 0, sizeof(class_mod)); 461 strncpy(class_mod.iface.ifname, clinfo->ifinfo->ifname, IFNAMSIZ); 462 class_mod.class_handle = clinfo->handle; 463 464 class_mod.pri = priq_clinfo->pri; 465 class_mod.qlimit = priq_clinfo->qlimit; 466 class_mod.flags = priq_clinfo->flags; 467 468 if (ioctl(priq_fd, PRIQ_MOD_CLASS, &class_mod) < 0) 469 return (QOPERR_SYSCALL); 470 return (0); 471 } 472 473 static int 474 priq_delete_class(struct classinfo *clinfo) 475 { 476 struct priq_delete_class class_delete; 477 478 if (clinfo->handle == PRIQ_NULLCLASS_HANDLE) 479 return (0); 480 481 memset(&class_delete, 0, sizeof(class_delete)); 482 strncpy(class_delete.iface.ifname, clinfo->ifinfo->ifname, 483 IFNAMSIZ); 484 class_delete.class_handle = clinfo->handle; 485 486 if (ioctl(priq_fd, PRIQ_DEL_CLASS, &class_delete) < 0) 487 return (QOPERR_SYSCALL); 488 return (0); 489 } 490 491 static int 492 priq_add_filter(struct fltrinfo *fltrinfo) 493 { 494 struct priq_add_filter fltr_add; 495 496 memset(&fltr_add, 0, sizeof(fltr_add)); 497 strncpy(fltr_add.iface.ifname, fltrinfo->clinfo->ifinfo->ifname, 498 IFNAMSIZ); 499 fltr_add.class_handle = fltrinfo->clinfo->handle; 500 fltr_add.filter = fltrinfo->fltr; 501 502 if (ioctl(priq_fd, PRIQ_ADD_FILTER, &fltr_add) < 0) 503 return (QOPERR_SYSCALL); 504 fltrinfo->handle = fltr_add.filter_handle; 505 return (0); 506 } 507 508 static int 509 priq_delete_filter(struct fltrinfo *fltrinfo) 510 { 511 struct priq_delete_filter fltr_del; 512 513 memset(&fltr_del, 0, sizeof(fltr_del)); 514 strncpy(fltr_del.iface.ifname, fltrinfo->clinfo->ifinfo->ifname, 515 IFNAMSIZ); 516 fltr_del.filter_handle = fltrinfo->handle; 517 518 if (ioctl(priq_fd, PRIQ_DEL_FILTER, &fltr_del) < 0) 519 return (QOPERR_SYSCALL); 520 return (0); 521 } 522 523 524