1/*	$NetBSD: qop_wfq.c,v 1.5 2002/03/05 04:11:53 itojun Exp $	*/
2/*	$KAME: qop_wfq.c,v 1.6 2001/12/03 08:20:56 kjc Exp $	*/
3/*
4 * Copyright (C) 1999-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_wfq.h>
50#include "altq_qop.h"
51#include "qop_wfq.h"
52
53static int wfq_attach(struct ifinfo *);
54static int wfq_detach(struct ifinfo *);
55static int wfq_enable(struct ifinfo *);
56static int wfq_disable(struct ifinfo *);
57
58#define WFQ_DEVICE	"/dev/altq/wfq"
59
60static int wfq_fd = -1;
61static int wfq_refcount = 0;
62
63static struct qdisc_ops wfq_qdisc = {
64	ALTQT_WFQ,
65	"wfq",
66	wfq_attach,
67	wfq_detach,
68	NULL,			/* clear */
69	wfq_enable,
70	wfq_disable,
71	NULL,			/* add class */
72	NULL,			/* modify class */
73	NULL,			/* delete class */
74	NULL,			/* add filter */
75	NULL			/* delete filter */
76};
77
78/*
79 * parser interface
80 */
81#define EQUAL(s1, s2)	(strcmp((s1), (s2)) == 0)
82
83int
84wfq_interface_parser(const char *ifname, int argc, char **argv)
85{
86	u_int  	bandwidth = 100000000;	/* 100Mbps */
87	u_int	tbrsize = 0;
88	int	hash_policy = 0;	/* 0: use default */
89	int	nqueues = 0;		/* 0: use default */
90	int	qsize = 0;		/* 0: use default */
91
92	/*
93	 * process options
94	 */
95	while (argc > 0) {
96		if (EQUAL(*argv, "bandwidth")) {
97			argc--; argv++;
98			if (argc > 0)
99				bandwidth = atobps(*argv);
100		} else if (EQUAL(*argv, "tbrsize")) {
101			argc--; argv++;
102			if (argc > 0)
103				tbrsize = atobytes(*argv);
104		} else if (EQUAL(*argv, "nqueues")) {
105			argc--; argv++;
106			if (argc > 0)
107				nqueues = (int)strtol(*argv, NULL, 0);
108		} else if (EQUAL(*argv, "qsize")) {
109			argc--; argv++;
110			if (argc > 0)
111				qsize = atobytes(*argv);
112		} else if (EQUAL(*argv, "hash")) {
113			argc--; argv++;
114			if (argc > 0) {
115				if (EQUAL(*argv, "dstaddr"))
116					hash_policy = WFQ_HASH_DSTADDR;
117				else if (EQUAL(*argv, "full"))
118					hash_policy = WFQ_HASH_FULL;
119				else if (EQUAL(*argv, "srcport"))
120					hash_policy = WFQ_HASH_SRCPORT;
121				else if (EQUAL(*argv, "srcaddr"))
122					hash_policy = WFQ_HASH_SRCADDR;
123				else {
124					LOG(LOG_ERR, 0,
125					    "Unknown hash policy '%s'", *argv);
126					return (0);
127				}
128			}
129		} else if (EQUAL(*argv, "wfq")) {
130			/* just skip */
131		} else {
132			LOG(LOG_ERR, 0, "Unknown keyword '%s'", *argv);
133			return (0);
134		}
135		argc--; argv++;
136	}
137
138	if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
139		return (0);
140
141	if (qsize != 0 && qsize < 1500) {
142		LOG(LOG_ERR, 0, "qsize too small: %d bytes", qsize);
143		return (0);
144	}
145
146	if (qcmd_wfq_add_if(ifname, bandwidth,
147			    hash_policy, nqueues, qsize) != 0)
148		return (0);
149	return (1);
150}
151
152/*
153 * qcmd api
154 */
155int
156qcmd_wfq_add_if(const char *ifname, u_int bandwidth, int hash_policy,
157		int nqueues, int qsize)
158{
159	int error;
160
161	error = qop_wfq_add_if(NULL, ifname, bandwidth,
162			       hash_policy, nqueues, qsize);
163	if (error != 0)
164		LOG(LOG_ERR, errno, "%s: can't add wfq on interface '%s'",
165		    qoperror(error), ifname);
166	return (error);
167}
168
169/*
170 * qop api
171 */
172int
173qop_wfq_add_if(struct ifinfo **rp, const char *ifname, u_int bandwidth,
174	       int hash_policy, int nqueues, int qsize)
175{
176	struct ifinfo *ifinfo = NULL;
177	struct wfq_ifinfo *wfq_ifinfo;
178	int error;
179
180	if ((wfq_ifinfo = calloc(1, sizeof(*wfq_ifinfo))) == NULL)
181		return (QOPERR_NOMEM);
182	wfq_ifinfo->hash_policy = hash_policy;
183	wfq_ifinfo->nqueues     = nqueues;
184	wfq_ifinfo->qsize       = qsize;
185
186	error = qop_add_if(&ifinfo, ifname, bandwidth,
187			   &wfq_qdisc, wfq_ifinfo);
188	if (error != 0) {
189		free(wfq_ifinfo);
190		return (error);
191	}
192
193	if (rp != NULL)
194		*rp = ifinfo;
195	return (0);
196}
197
198/*
199 *  system call interfaces for qdisc_ops
200 */
201static int
202wfq_attach(struct ifinfo *ifinfo)
203{
204	struct wfq_interface iface;
205	struct wfq_ifinfo *wfq_ifinfo;
206	struct wfq_conf conf;
207
208	if (wfq_fd < 0 &&
209	    (wfq_fd = open(WFQ_DEVICE, O_RDWR)) < 0 &&
210	    (wfq_fd = open_module(WFQ_DEVICE, O_RDWR)) < 0) {
211		LOG(LOG_ERR, errno, "WFQ open");
212		return (QOPERR_SYSCALL);
213	}
214
215	wfq_refcount++;
216	memset(&iface, 0, sizeof(iface));
217	strncpy(iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
218
219	if (ioctl(wfq_fd, WFQ_IF_ATTACH, &iface) < 0)
220		return (QOPERR_SYSCALL);
221
222	/* set wfq parameters */
223	wfq_ifinfo = (struct wfq_ifinfo *)ifinfo->private;
224	if (wfq_ifinfo->hash_policy != 0 || wfq_ifinfo->nqueues != 0 ||
225	    wfq_ifinfo->qsize != 0) {
226		memset(&conf, 0, sizeof(conf));
227		strncpy(conf.iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
228		conf.hash_policy = wfq_ifinfo->hash_policy;
229		conf.nqueues     = wfq_ifinfo->nqueues;
230		conf.qlimit      = wfq_ifinfo->qsize;
231		if (ioctl(wfq_fd, WFQ_CONFIG, &conf) < 0) {
232			LOG(LOG_ERR, errno, "WFQ_CONFIG");
233			return (QOPERR_SYSCALL);
234		}
235	}
236#if 1
237	LOG(LOG_INFO, 0, "wfq attached to %s", iface.wfq_ifacename);
238#endif
239	return (0);
240}
241
242static int
243wfq_detach(struct ifinfo *ifinfo)
244{
245	struct wfq_interface iface;
246
247	memset(&iface, 0, sizeof(iface));
248	strncpy(iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
249
250	if (ioctl(wfq_fd, WFQ_IF_DETACH, &iface) < 0)
251		return (QOPERR_SYSCALL);
252
253	if (--wfq_refcount == 0) {
254		close(wfq_fd);
255		wfq_fd = -1;
256	}
257	return (0);
258}
259
260static int
261wfq_enable(struct ifinfo *ifinfo)
262{
263	struct wfq_interface iface;
264
265	memset(&iface, 0, sizeof(iface));
266	strncpy(iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
267
268	if (ioctl(wfq_fd, WFQ_ENABLE, &iface) < 0)
269		return (QOPERR_SYSCALL);
270	return (0);
271}
272
273static int
274wfq_disable(struct ifinfo *ifinfo)
275{
276	struct wfq_interface iface;
277
278	memset(&iface, 0, sizeof(iface));
279	strncpy(iface.wfq_ifacename, ifinfo->ifname, IFNAMSIZ);
280
281	if (ioctl(wfq_fd, WFQ_DISABLE, &iface) < 0)
282		return (QOPERR_SYSCALL);
283	return (0);
284}
285