1/*	$NetBSD: altq_afmap.c,v 1.18 2007/03/04 05:59:00 christos Exp $	*/
2/*	$KAME: altq_afmap.c,v 1.12 2005/04/13 03:44:24 suz Exp $	*/
3
4/*
5 * Copyright (C) 1997-2002
6 *	Sony Computer Science Laboratories Inc.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/*
31 * experimental:
32 * mapping an ip flow to atm vpi/vci.
33 * this module is not related to queueing at all, but uses the altq
34 * flowinfo mechanism.  it's just put in the altq framework since
35 * it is easy to add devices to altq.
36 */
37
38#include <sys/cdefs.h>
39__KERNEL_RCSID(0, "$NetBSD: altq_afmap.c,v 1.18 2007/03/04 05:59:00 christos Exp $");
40
41#ifdef _KERNEL_OPT
42#include "opt_altq.h"
43#include "opt_inet.h"
44#endif
45
46#ifdef ALTQ_AFMAP
47
48#include <sys/param.h>
49#include <sys/malloc.h>
50#include <sys/mbuf.h>
51#include <sys/uio.h>
52#include <sys/socket.h>
53#include <sys/systm.h>
54#include <sys/proc.h>
55#include <sys/errno.h>
56#include <sys/time.h>
57#include <sys/kernel.h>
58#include <sys/kauth.h>
59
60#include <net/if.h>
61#include <net/if_types.h>
62#include <netinet/in.h>
63
64#include <altq/altq.h>
65#include <altq/altq_conf.h>
66#include <altq/altq_afmap.h>
67
68#ifdef ALTQ3_COMPAT
69
70LIST_HEAD(, afm_head) afhead_chain;
71
72static struct afm *afm_match4(struct afm_head *, struct flowinfo_in *);
73#ifdef INET6
74static struct afm *afm_match6(struct afm_head *, struct flowinfo_in6 *);
75#endif
76
77/*
78 * rules to block interrupts: afm_match can be called from a net
79 * level interrupt so that other routines handling the lists should
80 * be called in splnet().
81 */
82int
83afm_alloc(struct ifnet *ifp)
84{
85	struct afm_head *head;
86
87	head = malloc(sizeof(struct afm_head), M_DEVBUF, M_WAITOK|M_ZERO);
88	if (head == NULL)
89		panic("afm_alloc: malloc failed!");
90
91	/* initialize per interface afmap list */
92	LIST_INIT(&head->afh_head);
93
94	head->afh_ifp = ifp;
95
96	/* add this afm_head to the chain */
97	LIST_INSERT_HEAD(&afhead_chain, head, afh_chain);
98
99	return (0);
100}
101
102int
103afm_dealloc(struct ifnet *ifp)
104{
105	struct afm_head *head;
106
107	for (head = afhead_chain.lh_first; head != NULL;
108	     head = head->afh_chain.le_next)
109		if (head->afh_ifp == ifp)
110			break;
111	if (head == NULL)
112		return (-1);
113
114	afm_removeall(ifp);
115
116	LIST_REMOVE(head, afh_chain);
117
118	free(head, M_DEVBUF);
119	return 0;
120}
121
122struct afm *
123afm_top(struct ifnet *ifp)
124{
125	struct afm_head *head;
126
127	for (head = afhead_chain.lh_first; head != NULL;
128	     head = head->afh_chain.le_next)
129		if (head->afh_ifp == ifp)
130			break;
131	if (head == NULL)
132		return NULL;
133
134	return (head->afh_head.lh_first);
135}
136
137int
138afm_add(struct ifnet *ifp, struct atm_flowmap *flowmap)
139{
140	struct afm_head *head;
141	struct afm *afm;
142
143	for (head = afhead_chain.lh_first; head != NULL;
144	     head = head->afh_chain.le_next)
145		if (head->afh_ifp == ifp)
146			break;
147	if (head == NULL)
148		return (-1);
149
150	if (flowmap->af_flowinfo.fi_family == AF_INET) {
151		if (flowmap->af_flowinfo.fi_len != sizeof(struct flowinfo_in))
152			return (EINVAL);
153#ifdef INET6
154	} else if (flowmap->af_flowinfo.fi_family == AF_INET6) {
155		if (flowmap->af_flowinfo.fi_len != sizeof(struct flowinfo_in6))
156			return (EINVAL);
157#endif
158	} else
159		return (EINVAL);
160
161	afm = malloc(sizeof(struct afm), M_DEVBUF, M_WAITOK|M_ZERO);
162	if (afm == NULL)
163		return (ENOMEM);
164
165	afm->afm_vci = flowmap->af_vci;
166	afm->afm_vpi = flowmap->af_vpi;
167	(void)memcpy(&afm->afm_flowinfo, &flowmap->af_flowinfo,
168	      flowmap->af_flowinfo.fi_len);
169
170	LIST_INSERT_HEAD(&head->afh_head, afm, afm_list);
171	return 0;
172}
173
174int
175afm_remove(struct afm *afm)
176{
177	LIST_REMOVE(afm, afm_list);
178	free(afm, M_DEVBUF);
179	return (0);
180}
181
182int
183afm_removeall(struct ifnet *ifp)
184{
185	struct afm_head *head;
186	struct afm *afm;
187
188	for (head = afhead_chain.lh_first; head != NULL;
189	     head = head->afh_chain.le_next)
190		if (head->afh_ifp == ifp)
191			break;
192	if (head == NULL)
193		return (-1);
194
195	while ((afm = head->afh_head.lh_first) != NULL)
196		afm_remove(afm);
197	return (0);
198}
199
200struct afm *
201afm_lookup(struct ifnet *ifp, int vpi, int vci)
202{
203	struct afm_head *head;
204	struct afm *afm;
205
206	for (head = afhead_chain.lh_first; head != NULL;
207	     head = head->afh_chain.le_next)
208		if (head->afh_ifp == ifp)
209			break;
210	if (head == NULL)
211		return NULL;
212
213	for (afm = head->afh_head.lh_first; afm != NULL;
214	     afm = afm->afm_list.le_next)
215		if (afm->afm_vpi == vpi && afm->afm_vci == vci)
216			break;
217	return afm;
218}
219
220static struct afm *
221afm_match4(struct afm_head *head, struct flowinfo_in *fp)
222{
223	struct afm *afm;
224
225	for (afm = head->afh_head.lh_first; afm != NULL;
226	     afm = afm->afm_list.le_next) {
227		if (afm->afm_flowinfo4.fi_dst.s_addr != 0 &&
228		    afm->afm_flowinfo4.fi_dst.s_addr != fp->fi_dst.s_addr)
229			continue;
230		if (afm->afm_flowinfo4.fi_dport != 0 &&
231		    afm->afm_flowinfo4.fi_dport != fp->fi_dport)
232			continue;
233		if (afm->afm_flowinfo4.fi_src.s_addr != 0 &&
234		    afm->afm_flowinfo4.fi_src.s_addr != fp->fi_src.s_addr)
235			continue;
236		if (afm->afm_flowinfo4.fi_sport != 0 &&
237		    afm->afm_flowinfo4.fi_sport != fp->fi_sport)
238			continue;
239		if (afm->afm_flowinfo4.fi_proto != 0 &&
240		    afm->afm_flowinfo4.fi_proto != fp->fi_proto)
241			continue;
242		/* match found! */
243		return (afm);
244	}
245	return NULL;
246}
247
248#ifdef INET6
249static struct afm *
250afm_match6(struct afm_head *head, struct flowinfo_in6 *fp)
251{
252	struct afm *afm;
253
254	for (afm = head->afh_head.lh_first; afm != NULL;
255	     afm = afm->afm_list.le_next) {
256		if (afm->afm_flowinfo6.fi6_flowlabel != 0 &&
257		    afm->afm_flowinfo6.fi6_flowlabel != fp->fi6_flowlabel)
258			continue;
259#ifdef notyet
260		if (!IN6_IS_ADDR_UNSPECIFIED(&afm->afm_flowinfo6.fi6_dst) &&
261		    !IN6_ARE_ADDR_EQUAL(&afm->afm_flowinfo6.fi6_dst,
262					&fp->fi6_dst))
263			continue;
264		if (afm->afm_flowinfo6.fi6_dport != 0 &&
265		    afm->afm_flowinfo6.fi6_dport != fp->fi6_dport)
266			continue;
267#endif
268		if (!IN6_IS_ADDR_UNSPECIFIED(&afm->afm_flowinfo6.fi6_src) &&
269		    !IN6_ARE_ADDR_EQUAL(&afm->afm_flowinfo6.fi6_src,
270					&fp->fi6_src))
271			continue;
272#ifdef notyet
273		if (afm->afm_flowinfo6.fi6_sport != 0 &&
274		    afm->afm_flowinfo6.fi6_sport != fp->fi6_sport)
275			continue;
276#endif
277		if (afm->afm_flowinfo6.fi6_proto != 0 &&
278		    afm->afm_flowinfo6.fi6_proto != fp->fi6_proto)
279			continue;
280		/* match found! */
281		return (afm);
282	}
283	return NULL;
284}
285#endif
286
287/* should be called in splnet() */
288struct afm *
289afm_match(struct ifnet *ifp, struct flowinfo *flow)
290{
291	struct afm_head *head;
292
293	for (head = afhead_chain.lh_first; head != NULL;
294	     head = head->afh_chain.le_next)
295		if (head->afh_ifp == ifp)
296			break;
297	if (head == NULL)
298		return NULL;
299
300	switch (flow->fi_family) {
301	case AF_INET:
302		return (afm_match4(head, (struct flowinfo_in *)flow));
303
304#ifdef INET6
305	case AF_INET6:
306		return (afm_match6(head, (struct flowinfo_in6 *)flow));
307#endif
308
309	default:
310		return NULL;
311	}
312}
313
314/*
315 * afm device interface
316 */
317altqdev_decl(afm);
318
319int
320afmopen(dev_t dev, int flag, int fmt,
321    struct lwp *l)
322{
323	return 0;
324}
325
326int
327afmclose(dev_t dev, int flag, int fmt, struct lwp *l)
328{
329	int err, error = 0;
330	struct atm_flowmap fmap;
331	struct afm_head *head;
332
333	for (head = afhead_chain.lh_first; head != NULL;
334	     head = head->afh_chain.le_next) {
335
336		/* call interface to clean up maps */
337		sprintf(fmap.af_ifname, "%s", head->afh_ifp->if_xname);
338		err = afmioctl(dev, AFM_CLEANFMAP, (void *)&fmap, flag, l);
339		if (err && error == 0)
340			error = err;
341	}
342
343	return error;
344}
345
346int
347afmioctl(dev_t dev, ioctlcmd_t cmd, void *addr, int flag,
348    struct lwp *l)
349{
350	int	error = 0;
351	struct atm_flowmap *flowmap;
352	struct ifnet *ifp;
353
354	/* check cmd for superuser only */
355	switch (cmd) {
356	case AFM_GETFMAP:
357		break;
358	default:
359#if (__FreeBSD_version > 400000)
360		error = suser(p);
361#else
362		error = kauth_authorize_network(l->l_cred, KAUTH_NETWORK_ALTQ,
363		    KAUTH_REQ_NETWORK_ALTQ_AFMAP, NULL, NULL, NULL);
364#endif
365		if (error)
366			return (error);
367		break;
368	}
369
370	/* lookup interface */
371	flowmap = (struct atm_flowmap *)addr;
372	flowmap->af_ifname[IFNAMSIZ-1] = '\0';
373	ifp = ifunit(flowmap->af_ifname);
374	if (ifp == NULL || (ifp->if_flags & IFF_RUNNING) == 0)
375		error = ENXIO;
376	else
377		error = ifp->if_ioctl(ifp, cmd, addr);
378
379	return error;
380}
381
382#endif /* ALTQ3_COMPAT */
383#endif /* ALTQ_AFMAP */
384