xref: /netbsd-src/usr.sbin/altq/libaltq/qop_priq.c (revision 5e4c038a45edbc7d63b7c2daa76e29f88b64a4e3)
1 /*	$NetBSD: qop_priq.c,v 1.5 2002/03/05 04:11:53 itojun 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, *parent_clinfo;
309 	int error;
310 
311 	priq_clinfo = clinfo->private;
312 	if (clinfo->parent == NULL)
313 		return (QOPERR_CLASS_INVAL);
314 	parent_clinfo = clinfo->parent->private;
315 
316 	priq_clinfo->pri = pri;
317 	priq_clinfo->qlimit = qlimit;
318 	priq_clinfo->flags = flags;
319 
320 	error = qop_modify_class(clinfo, NULL);
321 	if (error == 0)
322 		return (0);
323 	return (error);
324 }
325 
326 /*
327  * sanity check at enabling priq:
328  *  1. there must one default class for an interface
329  */
330 static int
331 qop_priq_enable_hook(struct ifinfo *ifinfo)
332 {
333 	struct priq_ifinfo *priq_ifinfo;
334 
335 	priq_ifinfo = ifinfo->private;
336 	if (priq_ifinfo->default_class == NULL) {
337 		LOG(LOG_ERR, 0, "priq: no default class on interface %s!",
338 		    ifinfo->ifname);
339 		return (QOPERR_CLASS);
340 	}
341 	return (0);
342 }
343 
344 /*
345  *  system call interfaces for qdisc_ops
346  */
347 static int
348 priq_attach(struct ifinfo *ifinfo)
349 {
350 	struct priq_interface iface;
351 
352 	memset(&iface, 0, sizeof(iface));
353 	strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
354 
355 	if (priq_fd < 0 &&
356 	    (priq_fd = open(PRIQ_DEVICE, O_RDWR)) < 0 &&
357 	    (priq_fd = open_module(PRIQ_DEVICE, O_RDWR)) < 0) {
358 		LOG(LOG_ERR, errno, "PRIQ open");
359 		return (QOPERR_SYSCALL);
360 	}
361 
362 	priq_refcount++;
363 	memset(&iface, 0, sizeof(iface));
364 	strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
365 	iface.arg = ifinfo->bandwidth;
366 
367 	if (ioctl(priq_fd, PRIQ_IF_ATTACH, &iface) < 0)
368 		return (QOPERR_SYSCALL);
369 	return (0);
370 }
371 
372 static int
373 priq_detach(struct ifinfo *ifinfo)
374 {
375 	struct priq_interface iface;
376 
377 	memset(&iface, 0, sizeof(iface));
378 	strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
379 
380 	if (ioctl(priq_fd, PRIQ_IF_DETACH, &iface) < 0)
381 		return (QOPERR_SYSCALL);
382 
383 	if (--priq_refcount == 0) {
384 		close(priq_fd);
385 		priq_fd = -1;
386 	}
387 	return (0);
388 }
389 
390 static int
391 priq_clear(struct ifinfo *ifinfo)
392 {
393 	struct priq_interface iface;
394 
395 	memset(&iface, 0, sizeof(iface));
396 	strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
397 
398 	if (ioctl(priq_fd, PRIQ_CLEAR, &iface) < 0)
399 		return (QOPERR_SYSCALL);
400 	return (0);
401 }
402 
403 static int
404 priq_enable(struct ifinfo *ifinfo)
405 {
406 	struct priq_interface iface;
407 
408 	memset(&iface, 0, sizeof(iface));
409 	strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
410 
411 	if (ioctl(priq_fd, PRIQ_ENABLE, &iface) < 0)
412 		return (QOPERR_SYSCALL);
413 	return (0);
414 }
415 
416 static int
417 priq_disable(struct ifinfo *ifinfo)
418 {
419 	struct priq_interface iface;
420 
421 	memset(&iface, 0, sizeof(iface));
422 	strncpy(iface.ifname, ifinfo->ifname, IFNAMSIZ);
423 
424 	if (ioctl(priq_fd, PRIQ_DISABLE, &iface) < 0)
425 		return (QOPERR_SYSCALL);
426 	return (0);
427 }
428 
429 static int
430 priq_add_class(struct classinfo *clinfo)
431 {
432 	struct priq_add_class class_add;
433 	struct priq_classinfo *priq_clinfo;
434 	struct priq_ifinfo *priq_ifinfo;
435 
436 	priq_ifinfo = clinfo->ifinfo->private;
437 	priq_clinfo = clinfo->private;
438 
439 	memset(&class_add, 0, sizeof(class_add));
440 	strncpy(class_add.iface.ifname, clinfo->ifinfo->ifname, IFNAMSIZ);
441 
442 	class_add.pri = priq_clinfo->pri;
443 	class_add.qlimit = priq_clinfo->qlimit;
444 	class_add.flags = priq_clinfo->flags;
445 	if (ioctl(priq_fd, PRIQ_ADD_CLASS, &class_add) < 0) {
446 		clinfo->handle = PRIQ_NULLCLASS_HANDLE;
447 		return (QOPERR_SYSCALL);
448 	}
449 	clinfo->handle = class_add.class_handle;
450 	return (0);
451 }
452 
453 static int
454 priq_modify_class(struct classinfo *clinfo, void *arg)
455 {
456 	struct priq_modify_class class_mod;
457 	struct priq_classinfo *priq_clinfo;
458 
459 	priq_clinfo = clinfo->private;
460 
461 	memset(&class_mod, 0, sizeof(class_mod));
462 	strncpy(class_mod.iface.ifname, clinfo->ifinfo->ifname, IFNAMSIZ);
463 	class_mod.class_handle = clinfo->handle;
464 
465 	class_mod.pri = priq_clinfo->pri;
466 	class_mod.qlimit = priq_clinfo->qlimit;
467 	class_mod.flags = priq_clinfo->flags;
468 
469 	if (ioctl(priq_fd, PRIQ_MOD_CLASS, &class_mod) < 0)
470 		return (QOPERR_SYSCALL);
471 	return (0);
472 }
473 
474 static int
475 priq_delete_class(struct classinfo *clinfo)
476 {
477 	struct priq_delete_class class_delete;
478 
479 	if (clinfo->handle == PRIQ_NULLCLASS_HANDLE)
480 		return (0);
481 
482 	memset(&class_delete, 0, sizeof(class_delete));
483 	strncpy(class_delete.iface.ifname, clinfo->ifinfo->ifname,
484 		IFNAMSIZ);
485 	class_delete.class_handle = clinfo->handle;
486 
487 	if (ioctl(priq_fd, PRIQ_DEL_CLASS, &class_delete) < 0)
488 		return (QOPERR_SYSCALL);
489 	return (0);
490 }
491 
492 static int
493 priq_add_filter(struct fltrinfo *fltrinfo)
494 {
495 	struct priq_add_filter fltr_add;
496 
497 	memset(&fltr_add, 0, sizeof(fltr_add));
498 	strncpy(fltr_add.iface.ifname, fltrinfo->clinfo->ifinfo->ifname,
499 		IFNAMSIZ);
500 	fltr_add.class_handle = fltrinfo->clinfo->handle;
501 	fltr_add.filter = fltrinfo->fltr;
502 
503 	if (ioctl(priq_fd, PRIQ_ADD_FILTER, &fltr_add) < 0)
504 		return (QOPERR_SYSCALL);
505 	fltrinfo->handle = fltr_add.filter_handle;
506 	return (0);
507 }
508 
509 static int
510 priq_delete_filter(struct fltrinfo *fltrinfo)
511 {
512 	struct priq_delete_filter fltr_del;
513 
514 	memset(&fltr_del, 0, sizeof(fltr_del));
515 	strncpy(fltr_del.iface.ifname, fltrinfo->clinfo->ifinfo->ifname,
516 		IFNAMSIZ);
517 	fltr_del.filter_handle = fltrinfo->handle;
518 
519 	if (ioctl(priq_fd, PRIQ_DEL_FILTER, &fltr_del) < 0)
520 		return (QOPERR_SYSCALL);
521 	return (0);
522 }
523 
524 
525