1/*	$NetBSD: qop_priq.c,v 1.4 2001/08/22 08:52:37 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
53static int qop_priq_enable_hook(struct ifinfo *);
54
55static int priq_attach(struct ifinfo *);
56static int priq_detach(struct ifinfo *);
57static int priq_clear(struct ifinfo *);
58static int priq_enable(struct ifinfo *);
59static int priq_disable(struct ifinfo *);
60static int priq_add_class(struct classinfo *);
61static int priq_modify_class(struct classinfo *, void *);
62static int priq_delete_class(struct classinfo *);
63static int priq_add_filter(struct fltrinfo *);
64static int priq_delete_filter(struct fltrinfo *);
65
66#define PRIQ_DEVICE	"/dev/altq/priq"
67
68static int priq_fd = -1;
69static int priq_refcount = 0;
70
71static 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 */
91int
92priq_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
127int
128priq_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 */
179int
180qcmd_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
191int
192qcmd_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
211int
212qcmd_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 */
230int
231qop_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
262int
263qop_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
304int
305qop_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 */
330static int
331qop_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 */
347static int
348priq_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
372static int
373priq_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
390static int
391priq_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
403static int
404priq_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
416static int
417priq_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
429static int
430priq_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
453static int
454priq_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
474static int
475priq_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
492static int
493priq_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
509static int
510priq_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