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