xref: /netbsd-src/usr.sbin/altq/libaltq/qop_priq.c (revision 3b01aba77a7a698587faaae455bbfe740923c1f5)
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