qop_priq.c revision 1.2 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 *);
53
54 static int priq_attach(struct ifinfo *);
55 static int priq_detach(struct ifinfo *);
56 static int priq_clear(struct ifinfo *);
57 static int priq_enable(struct ifinfo *);
58 static int priq_disable(struct ifinfo *);
59 static int priq_add_class(struct classinfo *);
60 static int priq_modify_class(struct classinfo *, void *);
61 static int priq_delete_class(struct classinfo *);
62 static int priq_add_filter(struct fltrinfo *);
63 static int priq_delete_filter(struct 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