1130368Smlaier/*	$FreeBSD$	*/
2130365Smlaier/*	$KAME: altq_subr.c,v 1.21 2003/11/06 06:32:53 kjc Exp $	*/
3130365Smlaier
4130365Smlaier/*
5130365Smlaier * Copyright (C) 1997-2003
6130365Smlaier *	Sony Computer Science Laboratories Inc.  All rights reserved.
7130365Smlaier *
8130365Smlaier * Redistribution and use in source and binary forms, with or without
9130365Smlaier * modification, are permitted provided that the following conditions
10130365Smlaier * are met:
11130365Smlaier * 1. Redistributions of source code must retain the above copyright
12130365Smlaier *    notice, this list of conditions and the following disclaimer.
13130365Smlaier * 2. Redistributions in binary form must reproduce the above copyright
14130365Smlaier *    notice, this list of conditions and the following disclaimer in the
15130365Smlaier *    documentation and/or other materials provided with the distribution.
16130365Smlaier *
17130365Smlaier * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
18130365Smlaier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19130365Smlaier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20130365Smlaier * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
21130365Smlaier * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22130365Smlaier * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23130365Smlaier * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24130365Smlaier * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25130365Smlaier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26130365Smlaier * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27130365Smlaier * SUCH DAMAGE.
28130365Smlaier */
29130365Smlaier
30130365Smlaier#if defined(__FreeBSD__) || defined(__NetBSD__)
31130365Smlaier#include "opt_altq.h"
32130365Smlaier#include "opt_inet.h"
33130365Smlaier#ifdef __FreeBSD__
34130365Smlaier#include "opt_inet6.h"
35130365Smlaier#endif
36130365Smlaier#endif /* __FreeBSD__ || __NetBSD__ */
37130365Smlaier
38130365Smlaier#include <sys/param.h>
39130365Smlaier#include <sys/malloc.h>
40130365Smlaier#include <sys/mbuf.h>
41130365Smlaier#include <sys/systm.h>
42130365Smlaier#include <sys/proc.h>
43130365Smlaier#include <sys/socket.h>
44130365Smlaier#include <sys/socketvar.h>
45130365Smlaier#include <sys/kernel.h>
46130365Smlaier#include <sys/errno.h>
47130365Smlaier#include <sys/syslog.h>
48130365Smlaier#include <sys/sysctl.h>
49130365Smlaier#include <sys/queue.h>
50130365Smlaier
51130365Smlaier#include <net/if.h>
52130365Smlaier#include <net/if_dl.h>
53130365Smlaier#include <net/if_types.h>
54189106Sbz#ifdef __FreeBSD__
55185571Sbz#include <net/vnet.h>
56189106Sbz#endif
57130365Smlaier
58130365Smlaier#include <netinet/in.h>
59130365Smlaier#include <netinet/in_systm.h>
60130365Smlaier#include <netinet/ip.h>
61130365Smlaier#ifdef INET6
62130365Smlaier#include <netinet/ip6.h>
63130365Smlaier#endif
64130365Smlaier#include <netinet/tcp.h>
65130365Smlaier#include <netinet/udp.h>
66130365Smlaier
67130365Smlaier#include <net/pfvar.h>
68130365Smlaier#include <altq/altq.h>
69130365Smlaier#ifdef ALTQ3_COMPAT
70130365Smlaier#include <altq/altq_conf.h>
71130365Smlaier#endif
72130365Smlaier
73130365Smlaier/* machine dependent clock related includes */
74130365Smlaier#ifdef __FreeBSD__
75167905Snjl#include <sys/bus.h>
76167905Snjl#include <sys/cpu.h>
77167905Snjl#include <sys/eventhandler.h>
78130365Smlaier#include <machine/clock.h>
79130365Smlaier#endif
80219458Sjkim#if defined(__amd64__) || defined(__i386__)
81130365Smlaier#include <machine/cpufunc.h>		/* for pentium tsc */
82130365Smlaier#include <machine/specialreg.h>		/* for CPUID_TSC */
83219673Sjkim#ifdef __FreeBSD__
84219673Sjkim#include <machine/md_var.h>		/* for cpu_feature */
85219673Sjkim#elif defined(__NetBSD__) || defined(__OpenBSD__)
86219673Sjkim#include <machine/cpu.h>		/* for cpu_feature */
87130365Smlaier#endif
88219458Sjkim#endif /* __amd64 || __i386__ */
89130365Smlaier
90130365Smlaier/*
91130365Smlaier * internal function prototypes
92130365Smlaier */
93130365Smlaierstatic void	tbr_timeout(void *);
94130365Smlaierint (*altq_input)(struct mbuf *, int) = NULL;
95130508Smlaierstatic struct mbuf *tbr_dequeue(struct ifaltq *, int);
96130365Smlaierstatic int tbr_timer = 0;	/* token bucket regulator timer */
97142178Smlaier#if !defined(__FreeBSD__) || (__FreeBSD_version < 600000)
98130365Smlaierstatic struct callout tbr_callout = CALLOUT_INITIALIZER;
99142178Smlaier#else
100142178Smlaierstatic struct callout tbr_callout;
101142178Smlaier#endif
102130365Smlaier
103130365Smlaier#ifdef ALTQ3_CLFIER_COMPAT
104130365Smlaierstatic int 	extract_ports4(struct mbuf *, struct ip *, struct flowinfo_in *);
105130365Smlaier#ifdef INET6
106130365Smlaierstatic int 	extract_ports6(struct mbuf *, struct ip6_hdr *,
107130365Smlaier			       struct flowinfo_in6 *);
108130365Smlaier#endif
109130365Smlaierstatic int	apply_filter4(u_int32_t, struct flow_filter *,
110130365Smlaier			      struct flowinfo_in *);
111130365Smlaierstatic int	apply_ppfilter4(u_int32_t, struct flow_filter *,
112130365Smlaier				struct flowinfo_in *);
113130365Smlaier#ifdef INET6
114130365Smlaierstatic int	apply_filter6(u_int32_t, struct flow_filter6 *,
115130365Smlaier			      struct flowinfo_in6 *);
116130365Smlaier#endif
117130365Smlaierstatic int	apply_tosfilter4(u_int32_t, struct flow_filter *,
118130365Smlaier				 struct flowinfo_in *);
119130365Smlaierstatic u_long	get_filt_handle(struct acc_classifier *, int);
120130365Smlaierstatic struct acc_filter *filth_to_filtp(struct acc_classifier *, u_long);
121130365Smlaierstatic u_int32_t filt2fibmask(struct flow_filter *);
122130365Smlaier
123130365Smlaierstatic void 	ip4f_cache(struct ip *, struct flowinfo_in *);
124130365Smlaierstatic int 	ip4f_lookup(struct ip *, struct flowinfo_in *);
125130365Smlaierstatic int 	ip4f_init(void);
126130365Smlaierstatic struct ip4_frag	*ip4f_alloc(void);
127130365Smlaierstatic void 	ip4f_free(struct ip4_frag *);
128130365Smlaier#endif /* ALTQ3_CLFIER_COMPAT */
129130365Smlaier
130130365Smlaier/*
131130365Smlaier * alternate queueing support routines
132130365Smlaier */
133130365Smlaier
134130365Smlaier/* look up the queue state by the interface name and the queueing type. */
135130365Smlaiervoid *
136130365Smlaieraltq_lookup(name, type)
137130365Smlaier	char *name;
138130365Smlaier	int type;
139130365Smlaier{
140130365Smlaier	struct ifnet *ifp;
141130365Smlaier
142130365Smlaier	if ((ifp = ifunit(name)) != NULL) {
143130368Smlaier		/* read if_snd unlocked */
144130365Smlaier		if (type != ALTQT_NONE && ifp->if_snd.altq_type == type)
145130365Smlaier			return (ifp->if_snd.altq_disc);
146130365Smlaier	}
147130365Smlaier
148130365Smlaier	return NULL;
149130365Smlaier}
150130365Smlaier
151130365Smlaierint
152130365Smlaieraltq_attach(ifq, type, discipline, enqueue, dequeue, request, clfier, classify)
153130365Smlaier	struct ifaltq *ifq;
154130365Smlaier	int type;
155130365Smlaier	void *discipline;
156130365Smlaier	int (*enqueue)(struct ifaltq *, struct mbuf *, struct altq_pktattr *);
157130365Smlaier	struct mbuf *(*dequeue)(struct ifaltq *, int);
158130365Smlaier	int (*request)(struct ifaltq *, int, void *);
159130365Smlaier	void *clfier;
160130365Smlaier	void *(*classify)(void *, struct mbuf *, int);
161130365Smlaier{
162130368Smlaier	IFQ_LOCK(ifq);
163130368Smlaier	if (!ALTQ_IS_READY(ifq)) {
164130368Smlaier		IFQ_UNLOCK(ifq);
165130365Smlaier		return ENXIO;
166130368Smlaier	}
167130365Smlaier
168130365Smlaier#ifdef ALTQ3_COMPAT
169130365Smlaier	/*
170130365Smlaier	 * pfaltq can override the existing discipline, but altq3 cannot.
171130365Smlaier	 * check these if clfier is not NULL (which implies altq3).
172130365Smlaier	 */
173130365Smlaier	if (clfier != NULL) {
174130368Smlaier		if (ALTQ_IS_ENABLED(ifq)) {
175130368Smlaier			IFQ_UNLOCK(ifq);
176130365Smlaier			return EBUSY;
177130368Smlaier		}
178130368Smlaier		if (ALTQ_IS_ATTACHED(ifq)) {
179130368Smlaier			IFQ_UNLOCK(ifq);
180130365Smlaier			return EEXIST;
181130368Smlaier		}
182130365Smlaier	}
183130365Smlaier#endif
184130365Smlaier	ifq->altq_type     = type;
185130365Smlaier	ifq->altq_disc     = discipline;
186130365Smlaier	ifq->altq_enqueue  = enqueue;
187130365Smlaier	ifq->altq_dequeue  = dequeue;
188130365Smlaier	ifq->altq_request  = request;
189130365Smlaier	ifq->altq_clfier   = clfier;
190130365Smlaier	ifq->altq_classify = classify;
191130365Smlaier	ifq->altq_flags &= (ALTQF_CANTCHANGE|ALTQF_ENABLED);
192130365Smlaier#ifdef ALTQ3_COMPAT
193130365Smlaier#ifdef ALTQ_KLD
194130365Smlaier	altq_module_incref(type);
195130365Smlaier#endif
196130365Smlaier#endif
197130368Smlaier	IFQ_UNLOCK(ifq);
198130365Smlaier	return 0;
199130365Smlaier}
200130365Smlaier
201130365Smlaierint
202130365Smlaieraltq_detach(ifq)
203130365Smlaier	struct ifaltq *ifq;
204130365Smlaier{
205130368Smlaier	IFQ_LOCK(ifq);
206130368Smlaier
207130368Smlaier	if (!ALTQ_IS_READY(ifq)) {
208130368Smlaier		IFQ_UNLOCK(ifq);
209130365Smlaier		return ENXIO;
210130368Smlaier	}
211130368Smlaier	if (ALTQ_IS_ENABLED(ifq)) {
212130368Smlaier		IFQ_UNLOCK(ifq);
213130365Smlaier		return EBUSY;
214130368Smlaier	}
215130368Smlaier	if (!ALTQ_IS_ATTACHED(ifq)) {
216130368Smlaier		IFQ_UNLOCK(ifq);
217130365Smlaier		return (0);
218130368Smlaier	}
219130365Smlaier#ifdef ALTQ3_COMPAT
220130365Smlaier#ifdef ALTQ_KLD
221130365Smlaier	altq_module_declref(ifq->altq_type);
222130365Smlaier#endif
223130365Smlaier#endif
224130365Smlaier
225130365Smlaier	ifq->altq_type     = ALTQT_NONE;
226130365Smlaier	ifq->altq_disc     = NULL;
227130365Smlaier	ifq->altq_enqueue  = NULL;
228130365Smlaier	ifq->altq_dequeue  = NULL;
229130365Smlaier	ifq->altq_request  = NULL;
230130365Smlaier	ifq->altq_clfier   = NULL;
231130365Smlaier	ifq->altq_classify = NULL;
232130365Smlaier	ifq->altq_flags &= ALTQF_CANTCHANGE;
233130368Smlaier
234130368Smlaier	IFQ_UNLOCK(ifq);
235130365Smlaier	return 0;
236130365Smlaier}
237130365Smlaier
238130365Smlaierint
239130365Smlaieraltq_enable(ifq)
240130365Smlaier	struct ifaltq *ifq;
241130365Smlaier{
242130365Smlaier	int s;
243130365Smlaier
244130368Smlaier	IFQ_LOCK(ifq);
245130368Smlaier
246130368Smlaier	if (!ALTQ_IS_READY(ifq)) {
247130368Smlaier		IFQ_UNLOCK(ifq);
248130365Smlaier		return ENXIO;
249130368Smlaier	}
250130368Smlaier	if (ALTQ_IS_ENABLED(ifq)) {
251130368Smlaier		IFQ_UNLOCK(ifq);
252130365Smlaier		return 0;
253130368Smlaier	}
254130365Smlaier
255130365Smlaier#ifdef __NetBSD__
256130365Smlaier	s = splnet();
257130365Smlaier#else
258130365Smlaier	s = splimp();
259130365Smlaier#endif
260130368Smlaier	IFQ_PURGE_NOLOCK(ifq);
261130365Smlaier	ASSERT(ifq->ifq_len == 0);
262130550Smlaier	ifq->ifq_drv_maxlen = 0;		/* disable bulk dequeue */
263130365Smlaier	ifq->altq_flags |= ALTQF_ENABLED;
264130365Smlaier	if (ifq->altq_clfier != NULL)
265130365Smlaier		ifq->altq_flags |= ALTQF_CLASSIFY;
266130365Smlaier	splx(s);
267130365Smlaier
268130368Smlaier	IFQ_UNLOCK(ifq);
269130365Smlaier	return 0;
270130365Smlaier}
271130365Smlaier
272130365Smlaierint
273130365Smlaieraltq_disable(ifq)
274130365Smlaier	struct ifaltq *ifq;
275130365Smlaier{
276130365Smlaier	int s;
277130365Smlaier
278130368Smlaier	IFQ_LOCK(ifq);
279130368Smlaier	if (!ALTQ_IS_ENABLED(ifq)) {
280130368Smlaier		IFQ_UNLOCK(ifq);
281130365Smlaier		return 0;
282130368Smlaier	}
283130365Smlaier
284130365Smlaier#ifdef __NetBSD__
285130365Smlaier	s = splnet();
286130365Smlaier#else
287130365Smlaier	s = splimp();
288130365Smlaier#endif
289130368Smlaier	IFQ_PURGE_NOLOCK(ifq);
290130365Smlaier	ASSERT(ifq->ifq_len == 0);
291130365Smlaier	ifq->altq_flags &= ~(ALTQF_ENABLED|ALTQF_CLASSIFY);
292130365Smlaier	splx(s);
293130368Smlaier
294130368Smlaier	IFQ_UNLOCK(ifq);
295130365Smlaier	return 0;
296130365Smlaier}
297130365Smlaier
298130365Smlaier#ifdef ALTQ_DEBUG
299130365Smlaiervoid
300130365Smlaieraltq_assert(file, line, failedexpr)
301130365Smlaier	const char *file, *failedexpr;
302130365Smlaier	int line;
303130365Smlaier{
304130365Smlaier	(void)printf("altq assertion \"%s\" failed: file \"%s\", line %d\n",
305130365Smlaier		     failedexpr, file, line);
306130365Smlaier	panic("altq assertion");
307130365Smlaier	/* NOTREACHED */
308130365Smlaier}
309130365Smlaier#endif
310130365Smlaier
311130365Smlaier/*
312130365Smlaier * internal representation of token bucket parameters
313130365Smlaier *	rate:	byte_per_unittime << 32
314130365Smlaier *		(((bits_per_sec) / 8) << 32) / machclk_freq
315130365Smlaier *	depth:	byte << 32
316130365Smlaier *
317130365Smlaier */
318130365Smlaier#define	TBR_SHIFT	32
319130365Smlaier#define	TBR_SCALE(x)	((int64_t)(x) << TBR_SHIFT)
320130365Smlaier#define	TBR_UNSCALE(x)	((x) >> TBR_SHIFT)
321130365Smlaier
322130508Smlaierstatic struct mbuf *
323130365Smlaiertbr_dequeue(ifq, op)
324130365Smlaier	struct ifaltq *ifq;
325130365Smlaier	int op;
326130365Smlaier{
327130365Smlaier	struct tb_regulator *tbr;
328130365Smlaier	struct mbuf *m;
329130365Smlaier	int64_t interval;
330130365Smlaier	u_int64_t now;
331130365Smlaier
332130368Smlaier	IFQ_LOCK_ASSERT(ifq);
333130365Smlaier	tbr = ifq->altq_tbr;
334130365Smlaier	if (op == ALTDQ_REMOVE && tbr->tbr_lastop == ALTDQ_POLL) {
335130365Smlaier		/* if this is a remove after poll, bypass tbr check */
336130365Smlaier	} else {
337130365Smlaier		/* update token only when it is negative */
338130365Smlaier		if (tbr->tbr_token <= 0) {
339130365Smlaier			now = read_machclk();
340130365Smlaier			interval = now - tbr->tbr_last;
341130365Smlaier			if (interval >= tbr->tbr_filluptime)
342130365Smlaier				tbr->tbr_token = tbr->tbr_depth;
343130365Smlaier			else {
344130365Smlaier				tbr->tbr_token += interval * tbr->tbr_rate;
345130365Smlaier				if (tbr->tbr_token > tbr->tbr_depth)
346130365Smlaier					tbr->tbr_token = tbr->tbr_depth;
347130365Smlaier			}
348130365Smlaier			tbr->tbr_last = now;
349130365Smlaier		}
350130365Smlaier		/* if token is still negative, don't allow dequeue */
351130365Smlaier		if (tbr->tbr_token <= 0)
352130365Smlaier			return (NULL);
353130365Smlaier	}
354130365Smlaier
355130365Smlaier	if (ALTQ_IS_ENABLED(ifq))
356130365Smlaier		m = (*ifq->altq_dequeue)(ifq, op);
357130365Smlaier	else {
358130365Smlaier		if (op == ALTDQ_POLL)
359130368Smlaier			_IF_POLL(ifq, m);
360130365Smlaier		else
361130368Smlaier			_IF_DEQUEUE(ifq, m);
362130365Smlaier	}
363130365Smlaier
364130365Smlaier	if (m != NULL && op == ALTDQ_REMOVE)
365130365Smlaier		tbr->tbr_token -= TBR_SCALE(m_pktlen(m));
366130365Smlaier	tbr->tbr_lastop = op;
367130365Smlaier	return (m);
368130365Smlaier}
369130365Smlaier
370130365Smlaier/*
371130365Smlaier * set a token bucket regulator.
372130365Smlaier * if the specified rate is zero, the token bucket regulator is deleted.
373130365Smlaier */
374130365Smlaierint
375130365Smlaiertbr_set(ifq, profile)
376130365Smlaier	struct ifaltq *ifq;
377130365Smlaier	struct tb_profile *profile;
378130365Smlaier{
379130365Smlaier	struct tb_regulator *tbr, *otbr;
380130508Smlaier
381130508Smlaier	if (tbr_dequeue_ptr == NULL)
382130508Smlaier		tbr_dequeue_ptr = tbr_dequeue;
383130365Smlaier
384130365Smlaier	if (machclk_freq == 0)
385130365Smlaier		init_machclk();
386130365Smlaier	if (machclk_freq == 0) {
387130365Smlaier		printf("tbr_set: no cpu clock available!\n");
388130365Smlaier		return (ENXIO);
389130365Smlaier	}
390130365Smlaier
391130368Smlaier	IFQ_LOCK(ifq);
392130365Smlaier	if (profile->rate == 0) {
393130365Smlaier		/* delete this tbr */
394130368Smlaier		if ((tbr = ifq->altq_tbr) == NULL) {
395130368Smlaier			IFQ_UNLOCK(ifq);
396130365Smlaier			return (ENOENT);
397130368Smlaier		}
398130365Smlaier		ifq->altq_tbr = NULL;
399184205Sdes		free(tbr, M_DEVBUF);
400130368Smlaier		IFQ_UNLOCK(ifq);
401130365Smlaier		return (0);
402130365Smlaier	}
403130365Smlaier
404240233Sglebius	tbr = malloc(sizeof(struct tb_regulator), M_DEVBUF, M_NOWAIT | M_ZERO);
405240233Sglebius	if (tbr == NULL) {
406130368Smlaier		IFQ_UNLOCK(ifq);
407130365Smlaier		return (ENOMEM);
408130368Smlaier	}
409130365Smlaier
410130365Smlaier	tbr->tbr_rate = TBR_SCALE(profile->rate / 8) / machclk_freq;
411130365Smlaier	tbr->tbr_depth = TBR_SCALE(profile->depth);
412130365Smlaier	if (tbr->tbr_rate > 0)
413130365Smlaier		tbr->tbr_filluptime = tbr->tbr_depth / tbr->tbr_rate;
414130365Smlaier	else
415130365Smlaier		tbr->tbr_filluptime = 0xffffffffffffffffLL;
416130365Smlaier	tbr->tbr_token = tbr->tbr_depth;
417130365Smlaier	tbr->tbr_last = read_machclk();
418130365Smlaier	tbr->tbr_lastop = ALTDQ_REMOVE;
419130365Smlaier
420130365Smlaier	otbr = ifq->altq_tbr;
421130365Smlaier	ifq->altq_tbr = tbr;	/* set the new tbr */
422130365Smlaier
423130365Smlaier	if (otbr != NULL)
424184205Sdes		free(otbr, M_DEVBUF);
425130365Smlaier	else {
426130365Smlaier		if (tbr_timer == 0) {
427130365Smlaier			CALLOUT_RESET(&tbr_callout, 1, tbr_timeout, (void *)0);
428130365Smlaier			tbr_timer = 1;
429130365Smlaier		}
430130365Smlaier	}
431130368Smlaier	IFQ_UNLOCK(ifq);
432130365Smlaier	return (0);
433130365Smlaier}
434130365Smlaier
435130365Smlaier/*
436130365Smlaier * tbr_timeout goes through the interface list, and kicks the drivers
437130365Smlaier * if necessary.
438130368Smlaier *
439130368Smlaier * MPSAFE
440130365Smlaier */
441130365Smlaierstatic void
442130365Smlaiertbr_timeout(arg)
443130365Smlaier	void *arg;
444130365Smlaier{
445219457Sjkim#ifdef __FreeBSD__
446192278Sbz	VNET_ITERATOR_DECL(vnet_iter);
447192278Sbz#endif
448130365Smlaier	struct ifnet *ifp;
449130365Smlaier	int active, s;
450130365Smlaier
451130365Smlaier	active = 0;
452130365Smlaier#ifdef __NetBSD__
453130365Smlaier	s = splnet();
454130365Smlaier#else
455130365Smlaier	s = splimp();
456130365Smlaier#endif
457219457Sjkim#ifdef __FreeBSD__
458196481Srwatson	IFNET_RLOCK_NOSLEEP();
459196481Srwatson	VNET_LIST_RLOCK_NOSLEEP();
460192278Sbz	VNET_FOREACH(vnet_iter) {
461192278Sbz		CURVNET_SET(vnet_iter);
462130368Smlaier#endif
463192278Sbz		for (ifp = TAILQ_FIRST(&V_ifnet); ifp;
464192278Sbz		    ifp = TAILQ_NEXT(ifp, if_list)) {
465192278Sbz			/* read from if_snd unlocked */
466192278Sbz			if (!TBR_IS_ENABLED(&ifp->if_snd))
467192278Sbz				continue;
468192278Sbz			active++;
469192278Sbz			if (!IFQ_IS_EMPTY(&ifp->if_snd) &&
470192278Sbz			    ifp->if_start != NULL)
471192278Sbz				(*ifp->if_start)(ifp);
472192278Sbz		}
473219457Sjkim#ifdef __FreeBSD__
474192278Sbz		CURVNET_RESTORE();
475130365Smlaier	}
476196481Srwatson	VNET_LIST_RUNLOCK_NOSLEEP();
477196481Srwatson	IFNET_RUNLOCK_NOSLEEP();
478130368Smlaier#endif
479130365Smlaier	splx(s);
480130365Smlaier	if (active > 0)
481130365Smlaier		CALLOUT_RESET(&tbr_callout, 1, tbr_timeout, (void *)0);
482130365Smlaier	else
483130365Smlaier		tbr_timer = 0;	/* don't need tbr_timer anymore */
484130365Smlaier}
485130365Smlaier
486130365Smlaier/*
487130365Smlaier * get token bucket regulator profile
488130365Smlaier */
489130365Smlaierint
490130365Smlaiertbr_get(ifq, profile)
491130365Smlaier	struct ifaltq *ifq;
492130365Smlaier	struct tb_profile *profile;
493130365Smlaier{
494130365Smlaier	struct tb_regulator *tbr;
495130365Smlaier
496130368Smlaier	IFQ_LOCK(ifq);
497130365Smlaier	if ((tbr = ifq->altq_tbr) == NULL) {
498130365Smlaier		profile->rate = 0;
499130365Smlaier		profile->depth = 0;
500130365Smlaier	} else {
501130365Smlaier		profile->rate =
502130365Smlaier		    (u_int)TBR_UNSCALE(tbr->tbr_rate * 8 * machclk_freq);
503130365Smlaier		profile->depth = (u_int)TBR_UNSCALE(tbr->tbr_depth);
504130365Smlaier	}
505130368Smlaier	IFQ_UNLOCK(ifq);
506130365Smlaier	return (0);
507130365Smlaier}
508130365Smlaier
509130365Smlaier/*
510130365Smlaier * attach a discipline to the interface.  if one already exists, it is
511130365Smlaier * overridden.
512130368Smlaier * Locking is done in the discipline specific attach functions. Basically
513130368Smlaier * they call back to altq_attach which takes care of the attach and locking.
514130365Smlaier */
515130365Smlaierint
516130365Smlaieraltq_pfattach(struct pf_altq *a)
517130365Smlaier{
518130365Smlaier	int error = 0;
519130365Smlaier
520130365Smlaier	switch (a->scheduler) {
521130365Smlaier	case ALTQT_NONE:
522130365Smlaier		break;
523130365Smlaier#ifdef ALTQ_CBQ
524130365Smlaier	case ALTQT_CBQ:
525130365Smlaier		error = cbq_pfattach(a);
526130365Smlaier		break;
527130365Smlaier#endif
528130365Smlaier#ifdef ALTQ_PRIQ
529130365Smlaier	case ALTQT_PRIQ:
530130365Smlaier		error = priq_pfattach(a);
531130365Smlaier		break;
532130365Smlaier#endif
533130365Smlaier#ifdef ALTQ_HFSC
534130365Smlaier	case ALTQT_HFSC:
535130365Smlaier		error = hfsc_pfattach(a);
536130365Smlaier		break;
537130365Smlaier#endif
538130365Smlaier	default:
539130365Smlaier		error = ENXIO;
540130365Smlaier	}
541130365Smlaier
542130365Smlaier	return (error);
543130365Smlaier}
544130365Smlaier
545130365Smlaier/*
546130365Smlaier * detach a discipline from the interface.
547130365Smlaier * it is possible that the discipline was already overridden by another
548130365Smlaier * discipline.
549130365Smlaier */
550130365Smlaierint
551130365Smlaieraltq_pfdetach(struct pf_altq *a)
552130365Smlaier{
553130365Smlaier	struct ifnet *ifp;
554130365Smlaier	int s, error = 0;
555130365Smlaier
556130365Smlaier	if ((ifp = ifunit(a->ifname)) == NULL)
557130365Smlaier		return (EINVAL);
558130365Smlaier
559130365Smlaier	/* if this discipline is no longer referenced, just return */
560130368Smlaier	/* read unlocked from if_snd */
561130365Smlaier	if (a->altq_disc == NULL || a->altq_disc != ifp->if_snd.altq_disc)
562130365Smlaier		return (0);
563130365Smlaier
564130365Smlaier#ifdef __NetBSD__
565130365Smlaier	s = splnet();
566130365Smlaier#else
567130365Smlaier	s = splimp();
568130365Smlaier#endif
569130368Smlaier	/* read unlocked from if_snd, _disable and _detach take care */
570130365Smlaier	if (ALTQ_IS_ENABLED(&ifp->if_snd))
571130365Smlaier		error = altq_disable(&ifp->if_snd);
572130365Smlaier	if (error == 0)
573130365Smlaier		error = altq_detach(&ifp->if_snd);
574130365Smlaier	splx(s);
575130365Smlaier
576130365Smlaier	return (error);
577130365Smlaier}
578130365Smlaier
579130365Smlaier/*
580130365Smlaier * add a discipline or a queue
581130368Smlaier * Locking is done in the discipline specific functions with regards to
582130368Smlaier * malloc with WAITOK, also it is not yet clear which lock to use.
583130365Smlaier */
584130365Smlaierint
585130365Smlaieraltq_add(struct pf_altq *a)
586130365Smlaier{
587130365Smlaier	int error = 0;
588130365Smlaier
589130365Smlaier	if (a->qname[0] != 0)
590130365Smlaier		return (altq_add_queue(a));
591130365Smlaier
592130365Smlaier	if (machclk_freq == 0)
593130365Smlaier		init_machclk();
594130365Smlaier	if (machclk_freq == 0)
595130365Smlaier		panic("altq_add: no cpu clock");
596130365Smlaier
597130365Smlaier	switch (a->scheduler) {
598130365Smlaier#ifdef ALTQ_CBQ
599130365Smlaier	case ALTQT_CBQ:
600130365Smlaier		error = cbq_add_altq(a);
601130365Smlaier		break;
602130365Smlaier#endif
603130365Smlaier#ifdef ALTQ_PRIQ
604130365Smlaier	case ALTQT_PRIQ:
605130365Smlaier		error = priq_add_altq(a);
606130365Smlaier		break;
607130365Smlaier#endif
608130365Smlaier#ifdef ALTQ_HFSC
609130365Smlaier	case ALTQT_HFSC:
610130365Smlaier		error = hfsc_add_altq(a);
611130365Smlaier		break;
612130365Smlaier#endif
613130365Smlaier	default:
614130365Smlaier		error = ENXIO;
615130365Smlaier	}
616130365Smlaier
617130365Smlaier	return (error);
618130365Smlaier}
619130365Smlaier
620130365Smlaier/*
621130365Smlaier * remove a discipline or a queue
622130368Smlaier * It is yet unclear what lock to use to protect this operation, the
623130368Smlaier * discipline specific functions will determine and grab it
624130365Smlaier */
625130365Smlaierint
626130365Smlaieraltq_remove(struct pf_altq *a)
627130365Smlaier{
628130365Smlaier	int error = 0;
629130365Smlaier
630130365Smlaier	if (a->qname[0] != 0)
631130365Smlaier		return (altq_remove_queue(a));
632130365Smlaier
633130365Smlaier	switch (a->scheduler) {
634130365Smlaier#ifdef ALTQ_CBQ
635130365Smlaier	case ALTQT_CBQ:
636130365Smlaier		error = cbq_remove_altq(a);
637130365Smlaier		break;
638130365Smlaier#endif
639130365Smlaier#ifdef ALTQ_PRIQ
640130365Smlaier	case ALTQT_PRIQ:
641130365Smlaier		error = priq_remove_altq(a);
642130365Smlaier		break;
643130365Smlaier#endif
644130365Smlaier#ifdef ALTQ_HFSC
645130365Smlaier	case ALTQT_HFSC:
646130365Smlaier		error = hfsc_remove_altq(a);
647130365Smlaier		break;
648130365Smlaier#endif
649130365Smlaier	default:
650130365Smlaier		error = ENXIO;
651130365Smlaier	}
652130365Smlaier
653130365Smlaier	return (error);
654130365Smlaier}
655130365Smlaier
656130365Smlaier/*
657130365Smlaier * add a queue to the discipline
658130368Smlaier * It is yet unclear what lock to use to protect this operation, the
659130368Smlaier * discipline specific functions will determine and grab it
660130365Smlaier */
661130365Smlaierint
662130365Smlaieraltq_add_queue(struct pf_altq *a)
663130365Smlaier{
664130365Smlaier	int error = 0;
665130365Smlaier
666130365Smlaier	switch (a->scheduler) {
667130365Smlaier#ifdef ALTQ_CBQ
668130365Smlaier	case ALTQT_CBQ:
669130365Smlaier		error = cbq_add_queue(a);
670130365Smlaier		break;
671130365Smlaier#endif
672130365Smlaier#ifdef ALTQ_PRIQ
673130365Smlaier	case ALTQT_PRIQ:
674130365Smlaier		error = priq_add_queue(a);
675130365Smlaier		break;
676130365Smlaier#endif
677130365Smlaier#ifdef ALTQ_HFSC
678130365Smlaier	case ALTQT_HFSC:
679130365Smlaier		error = hfsc_add_queue(a);
680130365Smlaier		break;
681130365Smlaier#endif
682130365Smlaier	default:
683130365Smlaier		error = ENXIO;
684130365Smlaier	}
685130365Smlaier
686130365Smlaier	return (error);
687130365Smlaier}
688130365Smlaier
689130365Smlaier/*
690130365Smlaier * remove a queue from the discipline
691130368Smlaier * It is yet unclear what lock to use to protect this operation, the
692130368Smlaier * discipline specific functions will determine and grab it
693130365Smlaier */
694130365Smlaierint
695130365Smlaieraltq_remove_queue(struct pf_altq *a)
696130365Smlaier{
697130365Smlaier	int error = 0;
698130365Smlaier
699130365Smlaier	switch (a->scheduler) {
700130365Smlaier#ifdef ALTQ_CBQ
701130365Smlaier	case ALTQT_CBQ:
702130365Smlaier		error = cbq_remove_queue(a);
703130365Smlaier		break;
704130365Smlaier#endif
705130365Smlaier#ifdef ALTQ_PRIQ
706130365Smlaier	case ALTQT_PRIQ:
707130365Smlaier		error = priq_remove_queue(a);
708130365Smlaier		break;
709130365Smlaier#endif
710130365Smlaier#ifdef ALTQ_HFSC
711130365Smlaier	case ALTQT_HFSC:
712130365Smlaier		error = hfsc_remove_queue(a);
713130365Smlaier		break;
714130365Smlaier#endif
715130365Smlaier	default:
716130365Smlaier		error = ENXIO;
717130365Smlaier	}
718130365Smlaier
719130365Smlaier	return (error);
720130365Smlaier}
721130365Smlaier
722130365Smlaier/*
723130365Smlaier * get queue statistics
724130368Smlaier * Locking is done in the discipline specific functions with regards to
725130368Smlaier * copyout operations, also it is not yet clear which lock to use.
726130365Smlaier */
727130365Smlaierint
728130365Smlaieraltq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
729130365Smlaier{
730130365Smlaier	int error = 0;
731130365Smlaier
732130365Smlaier	switch (a->scheduler) {
733130365Smlaier#ifdef ALTQ_CBQ
734130365Smlaier	case ALTQT_CBQ:
735130365Smlaier		error = cbq_getqstats(a, ubuf, nbytes);
736130365Smlaier		break;
737130365Smlaier#endif
738130365Smlaier#ifdef ALTQ_PRIQ
739130365Smlaier	case ALTQT_PRIQ:
740130365Smlaier		error = priq_getqstats(a, ubuf, nbytes);
741130365Smlaier		break;
742130365Smlaier#endif
743130365Smlaier#ifdef ALTQ_HFSC
744130365Smlaier	case ALTQT_HFSC:
745130365Smlaier		error = hfsc_getqstats(a, ubuf, nbytes);
746130365Smlaier		break;
747130365Smlaier#endif
748130365Smlaier	default:
749130365Smlaier		error = ENXIO;
750130365Smlaier	}
751130365Smlaier
752130365Smlaier	return (error);
753130365Smlaier}
754130365Smlaier
755130365Smlaier/*
756130365Smlaier * read and write diffserv field in IPv4 or IPv6 header
757130365Smlaier */
758130365Smlaieru_int8_t
759130365Smlaierread_dsfield(m, pktattr)
760130365Smlaier	struct mbuf *m;
761130365Smlaier	struct altq_pktattr *pktattr;
762130365Smlaier{
763130365Smlaier	struct mbuf *m0;
764130365Smlaier	u_int8_t ds_field = 0;
765130365Smlaier
766130365Smlaier	if (pktattr == NULL ||
767130365Smlaier	    (pktattr->pattr_af != AF_INET && pktattr->pattr_af != AF_INET6))
768130365Smlaier		return ((u_int8_t)0);
769130365Smlaier
770130365Smlaier	/* verify that pattr_hdr is within the mbuf data */
771130365Smlaier	for (m0 = m; m0 != NULL; m0 = m0->m_next)
772130365Smlaier		if ((pktattr->pattr_hdr >= m0->m_data) &&
773130365Smlaier		    (pktattr->pattr_hdr < m0->m_data + m0->m_len))
774130365Smlaier			break;
775130365Smlaier	if (m0 == NULL) {
776130365Smlaier		/* ick, pattr_hdr is stale */
777130365Smlaier		pktattr->pattr_af = AF_UNSPEC;
778130365Smlaier#ifdef ALTQ_DEBUG
779130365Smlaier		printf("read_dsfield: can't locate header!\n");
780130365Smlaier#endif
781130365Smlaier		return ((u_int8_t)0);
782130365Smlaier	}
783130365Smlaier
784130365Smlaier	if (pktattr->pattr_af == AF_INET) {
785130365Smlaier		struct ip *ip = (struct ip *)pktattr->pattr_hdr;
786130365Smlaier
787130365Smlaier		if (ip->ip_v != 4)
788130365Smlaier			return ((u_int8_t)0);	/* version mismatch! */
789130365Smlaier		ds_field = ip->ip_tos;
790130365Smlaier	}
791130365Smlaier#ifdef INET6
792130365Smlaier	else if (pktattr->pattr_af == AF_INET6) {
793130365Smlaier		struct ip6_hdr *ip6 = (struct ip6_hdr *)pktattr->pattr_hdr;
794130365Smlaier		u_int32_t flowlabel;
795130365Smlaier
796130365Smlaier		flowlabel = ntohl(ip6->ip6_flow);
797130365Smlaier		if ((flowlabel >> 28) != 6)
798130365Smlaier			return ((u_int8_t)0);	/* version mismatch! */
799130365Smlaier		ds_field = (flowlabel >> 20) & 0xff;
800130365Smlaier	}
801130365Smlaier#endif
802130365Smlaier	return (ds_field);
803130365Smlaier}
804130365Smlaier
805130365Smlaiervoid
806189004Srdivackywrite_dsfield(struct mbuf *m, struct altq_pktattr *pktattr, u_int8_t dsfield)
807130365Smlaier{
808130365Smlaier	struct mbuf *m0;
809130365Smlaier
810130365Smlaier	if (pktattr == NULL ||
811130365Smlaier	    (pktattr->pattr_af != AF_INET && pktattr->pattr_af != AF_INET6))
812130365Smlaier		return;
813130365Smlaier
814130365Smlaier	/* verify that pattr_hdr is within the mbuf data */
815130365Smlaier	for (m0 = m; m0 != NULL; m0 = m0->m_next)
816130365Smlaier		if ((pktattr->pattr_hdr >= m0->m_data) &&
817130365Smlaier		    (pktattr->pattr_hdr < m0->m_data + m0->m_len))
818130365Smlaier			break;
819130365Smlaier	if (m0 == NULL) {
820130365Smlaier		/* ick, pattr_hdr is stale */
821130365Smlaier		pktattr->pattr_af = AF_UNSPEC;
822130365Smlaier#ifdef ALTQ_DEBUG
823130365Smlaier		printf("write_dsfield: can't locate header!\n");
824130365Smlaier#endif
825130365Smlaier		return;
826130365Smlaier	}
827130365Smlaier
828130365Smlaier	if (pktattr->pattr_af == AF_INET) {
829130365Smlaier		struct ip *ip = (struct ip *)pktattr->pattr_hdr;
830130365Smlaier		u_int8_t old;
831130365Smlaier		int32_t sum;
832130365Smlaier
833130365Smlaier		if (ip->ip_v != 4)
834130365Smlaier			return;		/* version mismatch! */
835130365Smlaier		old = ip->ip_tos;
836130365Smlaier		dsfield |= old & 3;	/* leave CU bits */
837130365Smlaier		if (old == dsfield)
838130365Smlaier			return;
839130365Smlaier		ip->ip_tos = dsfield;
840130365Smlaier		/*
841130365Smlaier		 * update checksum (from RFC1624)
842130365Smlaier		 *	   HC' = ~(~HC + ~m + m')
843130365Smlaier		 */
844130365Smlaier		sum = ~ntohs(ip->ip_sum) & 0xffff;
845130365Smlaier		sum += 0xff00 + (~old & 0xff) + dsfield;
846130365Smlaier		sum = (sum >> 16) + (sum & 0xffff);
847130365Smlaier		sum += (sum >> 16);  /* add carry */
848130365Smlaier
849130365Smlaier		ip->ip_sum = htons(~sum & 0xffff);
850130365Smlaier	}
851130365Smlaier#ifdef INET6
852130365Smlaier	else if (pktattr->pattr_af == AF_INET6) {
853130365Smlaier		struct ip6_hdr *ip6 = (struct ip6_hdr *)pktattr->pattr_hdr;
854130365Smlaier		u_int32_t flowlabel;
855130365Smlaier
856130365Smlaier		flowlabel = ntohl(ip6->ip6_flow);
857130365Smlaier		if ((flowlabel >> 28) != 6)
858130365Smlaier			return;		/* version mismatch! */
859130365Smlaier		flowlabel = (flowlabel & 0xf03fffff) | (dsfield << 20);
860130365Smlaier		ip6->ip6_flow = htonl(flowlabel);
861130365Smlaier	}
862130365Smlaier#endif
863130365Smlaier	return;
864130365Smlaier}
865130365Smlaier
866130365Smlaier
867130365Smlaier/*
868130365Smlaier * high resolution clock support taking advantage of a machine dependent
869130365Smlaier * high resolution time counter (e.g., timestamp counter of intel pentium).
870130365Smlaier * we assume
871130365Smlaier *  - 64-bit-long monotonically-increasing counter
872130365Smlaier *  - frequency range is 100M-4GHz (CPU speed)
873130365Smlaier */
874130365Smlaier/* if pcc is not available or disabled, emulate 256MHz using microtime() */
875130365Smlaier#define	MACHCLK_SHIFT	8
876130365Smlaier
877130365Smlaierint machclk_usepcc;
878171407Snjlu_int32_t machclk_freq;
879171407Snjlu_int32_t machclk_per_tick;
880130365Smlaier
881130365Smlaier#if defined(__i386__) && defined(__NetBSD__)
882130365Smlaierextern u_int64_t cpu_tsc_freq;
883219459Sjkim#endif
884130365Smlaier
885167905Snjl#if (__FreeBSD_version >= 700035)
886167905Snjl/* Update TSC freq with the value indicated by the caller. */
887167905Snjlstatic void
888167905Snjltsc_freq_changed(void *arg, const struct cf_level *level, int status)
889167905Snjl{
890167905Snjl	/* If there was an error during the transition, don't do anything. */
891167905Snjl	if (status != 0)
892167905Snjl		return;
893167905Snjl
894187566Sjkim#if (__FreeBSD_version >= 701102) && (defined(__amd64__) || defined(__i386__))
895184102Sjkim	/* If TSC is P-state invariant, don't do anything. */
896184102Sjkim	if (tsc_is_invariant)
897184102Sjkim		return;
898184102Sjkim#endif
899184102Sjkim
900167905Snjl	/* Total setting for this level gives the new frequency in MHz. */
901171407Snjl	init_machclk();
902167905Snjl}
903167905SnjlEVENTHANDLER_DEFINE(cpufreq_post_change, tsc_freq_changed, NULL,
904171407Snjl    EVENTHANDLER_PRI_LAST);
905167905Snjl#endif /* __FreeBSD_version >= 700035 */
906167905Snjl
907171407Snjlstatic void
908171407Snjlinit_machclk_setup(void)
909130365Smlaier{
910142201Sgreen#if (__FreeBSD_version >= 600000)
911142201Sgreen	callout_init(&tbr_callout, 0);
912142201Sgreen#endif
913142201Sgreen
914130365Smlaier	machclk_usepcc = 1;
915130365Smlaier
916219459Sjkim#if (!defined(__amd64__) && !defined(__i386__)) || defined(ALTQ_NOPCC)
917130365Smlaier	machclk_usepcc = 0;
918130365Smlaier#endif
919130365Smlaier#if defined(__FreeBSD__) && defined(SMP)
920130365Smlaier	machclk_usepcc = 0;
921130365Smlaier#endif
922130365Smlaier#if defined(__NetBSD__) && defined(MULTIPROCESSOR)
923130365Smlaier	machclk_usepcc = 0;
924130365Smlaier#endif
925219458Sjkim#if defined(__amd64__) || defined(__i386__)
926130365Smlaier	/* check if TSC is available */
927219461Sjkim#ifdef __FreeBSD__
928220433Sjkim	if ((cpu_feature & CPUID_TSC) == 0 ||
929220433Sjkim	    atomic_load_acq_64(&tsc_freq) == 0)
930219461Sjkim#else
931219461Sjkim	if ((cpu_feature & CPUID_TSC) == 0)
932219461Sjkim#endif
933130365Smlaier		machclk_usepcc = 0;
934130365Smlaier#endif
935171407Snjl}
936130365Smlaier
937171407Snjlvoid
938171407Snjlinit_machclk(void)
939171407Snjl{
940171407Snjl	static int called;
941171407Snjl
942171407Snjl	/* Call one-time initialization function. */
943171407Snjl	if (!called) {
944171407Snjl		init_machclk_setup();
945171407Snjl		called = 1;
946171407Snjl	}
947171407Snjl
948130365Smlaier	if (machclk_usepcc == 0) {
949130365Smlaier		/* emulate 256MHz using microtime() */
950130365Smlaier		machclk_freq = 1000000 << MACHCLK_SHIFT;
951130365Smlaier		machclk_per_tick = machclk_freq / hz;
952130365Smlaier#ifdef ALTQ_DEBUG
953130365Smlaier		printf("altq: emulate %uHz cpu clock\n", machclk_freq);
954130365Smlaier#endif
955130365Smlaier		return;
956130365Smlaier	}
957130365Smlaier
958130365Smlaier	/*
959130365Smlaier	 * if the clock frequency (of Pentium TSC or Alpha PCC) is
960130365Smlaier	 * accessible, just use it.
961130365Smlaier	 */
962219458Sjkim#if defined(__amd64__) || defined(__i386__)
963130365Smlaier#ifdef __FreeBSD__
964220433Sjkim	machclk_freq = atomic_load_acq_64(&tsc_freq);
965130365Smlaier#elif defined(__NetBSD__)
966130365Smlaier	machclk_freq = (u_int32_t)cpu_tsc_freq;
967130365Smlaier#elif defined(__OpenBSD__) && (defined(I586_CPU) || defined(I686_CPU))
968130365Smlaier	machclk_freq = pentium_mhz * 1000000;
969130365Smlaier#endif
970130365Smlaier#endif
971130365Smlaier
972130365Smlaier	/*
973130365Smlaier	 * if we don't know the clock frequency, measure it.
974130365Smlaier	 */
975130365Smlaier	if (machclk_freq == 0) {
976130365Smlaier		static int	wait;
977130365Smlaier		struct timeval	tv_start, tv_end;
978130365Smlaier		u_int64_t	start, end, diff;
979130365Smlaier		int		timo;
980130365Smlaier
981130365Smlaier		microtime(&tv_start);
982130365Smlaier		start = read_machclk();
983130365Smlaier		timo = hz;	/* 1 sec */
984130365Smlaier		(void)tsleep(&wait, PWAIT | PCATCH, "init_machclk", timo);
985130365Smlaier		microtime(&tv_end);
986130365Smlaier		end = read_machclk();
987130365Smlaier		diff = (u_int64_t)(tv_end.tv_sec - tv_start.tv_sec) * 1000000
988130365Smlaier		    + tv_end.tv_usec - tv_start.tv_usec;
989130365Smlaier		if (diff != 0)
990130365Smlaier			machclk_freq = (u_int)((end - start) * 1000000 / diff);
991130365Smlaier	}
992130365Smlaier
993130365Smlaier	machclk_per_tick = machclk_freq / hz;
994130365Smlaier
995130365Smlaier#ifdef ALTQ_DEBUG
996130365Smlaier	printf("altq: CPU clock: %uHz\n", machclk_freq);
997130365Smlaier#endif
998130365Smlaier}
999130365Smlaier
1000130365Smlaier#if defined(__OpenBSD__) && defined(__i386__)
1001130365Smlaierstatic __inline u_int64_t
1002130365Smlaierrdtsc(void)
1003130365Smlaier{
1004130365Smlaier	u_int64_t rv;
1005130365Smlaier	__asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
1006130365Smlaier	return (rv);
1007130365Smlaier}
1008130365Smlaier#endif /* __OpenBSD__ && __i386__ */
1009130365Smlaier
1010130365Smlaieru_int64_t
1011130365Smlaierread_machclk(void)
1012130365Smlaier{
1013130365Smlaier	u_int64_t val;
1014130365Smlaier
1015130365Smlaier	if (machclk_usepcc) {
1016219458Sjkim#if defined(__amd64__) || defined(__i386__)
1017130365Smlaier		val = rdtsc();
1018130365Smlaier#else
1019130365Smlaier		panic("read_machclk");
1020130365Smlaier#endif
1021130365Smlaier	} else {
1022130365Smlaier		struct timeval tv;
1023130365Smlaier
1024130365Smlaier		microtime(&tv);
1025130365Smlaier		val = (((u_int64_t)(tv.tv_sec - boottime.tv_sec) * 1000000
1026130365Smlaier		    + tv.tv_usec) << MACHCLK_SHIFT);
1027130365Smlaier	}
1028130365Smlaier	return (val);
1029130365Smlaier}
1030130365Smlaier
1031130365Smlaier#ifdef ALTQ3_CLFIER_COMPAT
1032130365Smlaier
1033130365Smlaier#ifndef IPPROTO_ESP
1034130365Smlaier#define	IPPROTO_ESP	50		/* encapsulating security payload */
1035130365Smlaier#endif
1036130365Smlaier#ifndef IPPROTO_AH
1037130365Smlaier#define	IPPROTO_AH	51		/* authentication header */
1038130365Smlaier#endif
1039130365Smlaier
1040130365Smlaier/*
1041130365Smlaier * extract flow information from a given packet.
1042130365Smlaier * filt_mask shows flowinfo fields required.
1043130365Smlaier * we assume the ip header is in one mbuf, and addresses and ports are
1044130365Smlaier * in network byte order.
1045130365Smlaier */
1046130365Smlaierint
1047130365Smlaieraltq_extractflow(m, af, flow, filt_bmask)
1048130365Smlaier	struct mbuf *m;
1049130365Smlaier	int af;
1050130365Smlaier	struct flowinfo *flow;
1051130365Smlaier	u_int32_t	filt_bmask;
1052130365Smlaier{
1053130365Smlaier
1054130365Smlaier	switch (af) {
1055130365Smlaier	case PF_INET: {
1056130365Smlaier		struct flowinfo_in *fin;
1057130365Smlaier		struct ip *ip;
1058130365Smlaier
1059130365Smlaier		ip = mtod(m, struct ip *);
1060130365Smlaier
1061130365Smlaier		if (ip->ip_v != 4)
1062130365Smlaier			break;
1063130365Smlaier
1064130365Smlaier		fin = (struct flowinfo_in *)flow;
1065130365Smlaier		fin->fi_len = sizeof(struct flowinfo_in);
1066130365Smlaier		fin->fi_family = AF_INET;
1067130365Smlaier
1068130365Smlaier		fin->fi_proto = ip->ip_p;
1069130365Smlaier		fin->fi_tos = ip->ip_tos;
1070130365Smlaier
1071130365Smlaier		fin->fi_src.s_addr = ip->ip_src.s_addr;
1072130365Smlaier		fin->fi_dst.s_addr = ip->ip_dst.s_addr;
1073130365Smlaier
1074130365Smlaier		if (filt_bmask & FIMB4_PORTS)
1075130365Smlaier			/* if port info is required, extract port numbers */
1076130365Smlaier			extract_ports4(m, ip, fin);
1077130365Smlaier		else {
1078130365Smlaier			fin->fi_sport = 0;
1079130365Smlaier			fin->fi_dport = 0;
1080130365Smlaier			fin->fi_gpi = 0;
1081130365Smlaier		}
1082130365Smlaier		return (1);
1083130365Smlaier	}
1084130365Smlaier
1085130365Smlaier#ifdef INET6
1086130365Smlaier	case PF_INET6: {
1087130365Smlaier		struct flowinfo_in6 *fin6;
1088130365Smlaier		struct ip6_hdr *ip6;
1089130365Smlaier
1090130365Smlaier		ip6 = mtod(m, struct ip6_hdr *);
1091130365Smlaier		/* should we check the ip version? */
1092130365Smlaier
1093130365Smlaier		fin6 = (struct flowinfo_in6 *)flow;
1094130365Smlaier		fin6->fi6_len = sizeof(struct flowinfo_in6);
1095130365Smlaier		fin6->fi6_family = AF_INET6;
1096130365Smlaier
1097130365Smlaier		fin6->fi6_proto = ip6->ip6_nxt;
1098130365Smlaier		fin6->fi6_tclass   = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
1099130365Smlaier
1100130365Smlaier		fin6->fi6_flowlabel = ip6->ip6_flow & htonl(0x000fffff);
1101130365Smlaier		fin6->fi6_src = ip6->ip6_src;
1102130365Smlaier		fin6->fi6_dst = ip6->ip6_dst;
1103130365Smlaier
1104130365Smlaier		if ((filt_bmask & FIMB6_PORTS) ||
1105130365Smlaier		    ((filt_bmask & FIMB6_PROTO)
1106130365Smlaier		     && ip6->ip6_nxt > IPPROTO_IPV6))
1107130365Smlaier			/*
1108130365Smlaier			 * if port info is required, or proto is required
1109130365Smlaier			 * but there are option headers, extract port
1110130365Smlaier			 * and protocol numbers.
1111130365Smlaier			 */
1112130365Smlaier			extract_ports6(m, ip6, fin6);
1113130365Smlaier		else {
1114130365Smlaier			fin6->fi6_sport = 0;
1115130365Smlaier			fin6->fi6_dport = 0;
1116130365Smlaier			fin6->fi6_gpi = 0;
1117130365Smlaier		}
1118130365Smlaier		return (1);
1119130365Smlaier	}
1120130365Smlaier#endif /* INET6 */
1121130365Smlaier
1122130365Smlaier	default:
1123130365Smlaier		break;
1124130365Smlaier	}
1125130365Smlaier
1126130365Smlaier	/* failed */
1127130365Smlaier	flow->fi_len = sizeof(struct flowinfo);
1128130365Smlaier	flow->fi_family = AF_UNSPEC;
1129130365Smlaier	return (0);
1130130365Smlaier}
1131130365Smlaier
1132130365Smlaier/*
1133130365Smlaier * helper routine to extract port numbers
1134130365Smlaier */
1135130365Smlaier/* structure for ipsec and ipv6 option header template */
1136130365Smlaierstruct _opt6 {
1137130365Smlaier	u_int8_t	opt6_nxt;	/* next header */
1138130365Smlaier	u_int8_t	opt6_hlen;	/* header extension length */
1139130365Smlaier	u_int16_t	_pad;
1140130365Smlaier	u_int32_t	ah_spi;		/* security parameter index
1141130365Smlaier					   for authentication header */
1142130365Smlaier};
1143130365Smlaier
1144130365Smlaier/*
1145130365Smlaier * extract port numbers from a ipv4 packet.
1146130365Smlaier */
1147130365Smlaierstatic int
1148130365Smlaierextract_ports4(m, ip, fin)
1149130365Smlaier	struct mbuf *m;
1150130365Smlaier	struct ip *ip;
1151130365Smlaier	struct flowinfo_in *fin;
1152130365Smlaier{
1153130365Smlaier	struct mbuf *m0;
1154130365Smlaier	u_short ip_off;
1155130365Smlaier	u_int8_t proto;
1156130365Smlaier	int 	off;
1157130365Smlaier
1158130365Smlaier	fin->fi_sport = 0;
1159130365Smlaier	fin->fi_dport = 0;
1160130365Smlaier	fin->fi_gpi = 0;
1161130365Smlaier
1162130365Smlaier	ip_off = ntohs(ip->ip_off);
1163130365Smlaier	/* if it is a fragment, try cached fragment info */
1164130365Smlaier	if (ip_off & IP_OFFMASK) {
1165130365Smlaier		ip4f_lookup(ip, fin);
1166130365Smlaier		return (1);
1167130365Smlaier	}
1168130365Smlaier
1169130365Smlaier	/* locate the mbuf containing the protocol header */
1170130365Smlaier	for (m0 = m; m0 != NULL; m0 = m0->m_next)
1171130365Smlaier		if (((caddr_t)ip >= m0->m_data) &&
1172130365Smlaier		    ((caddr_t)ip < m0->m_data + m0->m_len))
1173130365Smlaier			break;
1174130365Smlaier	if (m0 == NULL) {
1175130365Smlaier#ifdef ALTQ_DEBUG
1176130365Smlaier		printf("extract_ports4: can't locate header! ip=%p\n", ip);
1177130365Smlaier#endif
1178130365Smlaier		return (0);
1179130365Smlaier	}
1180130365Smlaier	off = ((caddr_t)ip - m0->m_data) + (ip->ip_hl << 2);
1181130365Smlaier	proto = ip->ip_p;
1182130365Smlaier
1183130365Smlaier#ifdef ALTQ_IPSEC
1184130365Smlaier again:
1185130365Smlaier#endif
1186130365Smlaier	while (off >= m0->m_len) {
1187130365Smlaier		off -= m0->m_len;
1188130365Smlaier		m0 = m0->m_next;
1189130365Smlaier		if (m0 == NULL)
1190130365Smlaier			return (0);  /* bogus ip_hl! */
1191130365Smlaier	}
1192130365Smlaier	if (m0->m_len < off + 4)
1193130365Smlaier		return (0);
1194130365Smlaier
1195130365Smlaier	switch (proto) {
1196130365Smlaier	case IPPROTO_TCP:
1197130365Smlaier	case IPPROTO_UDP: {
1198130365Smlaier		struct udphdr *udp;
1199130365Smlaier
1200130365Smlaier		udp = (struct udphdr *)(mtod(m0, caddr_t) + off);
1201130365Smlaier		fin->fi_sport = udp->uh_sport;
1202130365Smlaier		fin->fi_dport = udp->uh_dport;
1203130365Smlaier		fin->fi_proto = proto;
1204130365Smlaier		}
1205130365Smlaier		break;
1206130365Smlaier
1207130365Smlaier#ifdef ALTQ_IPSEC
1208130365Smlaier	case IPPROTO_ESP:
1209130365Smlaier		if (fin->fi_gpi == 0){
1210130365Smlaier			u_int32_t *gpi;
1211130365Smlaier
1212130365Smlaier			gpi = (u_int32_t *)(mtod(m0, caddr_t) + off);
1213130365Smlaier			fin->fi_gpi   = *gpi;
1214130365Smlaier		}
1215130365Smlaier		fin->fi_proto = proto;
1216130365Smlaier		break;
1217130365Smlaier
1218130365Smlaier	case IPPROTO_AH: {
1219130365Smlaier			/* get next header and header length */
1220130365Smlaier			struct _opt6 *opt6;
1221130365Smlaier
1222130365Smlaier			opt6 = (struct _opt6 *)(mtod(m0, caddr_t) + off);
1223130365Smlaier			proto = opt6->opt6_nxt;
1224130365Smlaier			off += 8 + (opt6->opt6_hlen * 4);
1225130365Smlaier			if (fin->fi_gpi == 0 && m0->m_len >= off + 8)
1226130365Smlaier				fin->fi_gpi = opt6->ah_spi;
1227130365Smlaier		}
1228130365Smlaier		/* goto the next header */
1229130365Smlaier		goto again;
1230130365Smlaier#endif  /* ALTQ_IPSEC */
1231130365Smlaier
1232130365Smlaier	default:
1233130365Smlaier		fin->fi_proto = proto;
1234130365Smlaier		return (0);
1235130365Smlaier	}
1236130365Smlaier
1237130365Smlaier	/* if this is a first fragment, cache it. */
1238130365Smlaier	if (ip_off & IP_MF)
1239130365Smlaier		ip4f_cache(ip, fin);
1240130365Smlaier
1241130365Smlaier	return (1);
1242130365Smlaier}
1243130365Smlaier
1244130365Smlaier#ifdef INET6
1245130365Smlaierstatic int
1246130365Smlaierextract_ports6(m, ip6, fin6)
1247130365Smlaier	struct mbuf *m;
1248130365Smlaier	struct ip6_hdr *ip6;
1249130365Smlaier	struct flowinfo_in6 *fin6;
1250130365Smlaier{
1251130365Smlaier	struct mbuf *m0;
1252130365Smlaier	int	off;
1253130365Smlaier	u_int8_t proto;
1254130365Smlaier
1255130365Smlaier	fin6->fi6_gpi   = 0;
1256130365Smlaier	fin6->fi6_sport = 0;
1257130365Smlaier	fin6->fi6_dport = 0;
1258130365Smlaier
1259130365Smlaier	/* locate the mbuf containing the protocol header */
1260130365Smlaier	for (m0 = m; m0 != NULL; m0 = m0->m_next)
1261130365Smlaier		if (((caddr_t)ip6 >= m0->m_data) &&
1262130365Smlaier		    ((caddr_t)ip6 < m0->m_data + m0->m_len))
1263130365Smlaier			break;
1264130365Smlaier	if (m0 == NULL) {
1265130365Smlaier#ifdef ALTQ_DEBUG
1266130365Smlaier		printf("extract_ports6: can't locate header! ip6=%p\n", ip6);
1267130365Smlaier#endif
1268130365Smlaier		return (0);
1269130365Smlaier	}
1270130365Smlaier	off = ((caddr_t)ip6 - m0->m_data) + sizeof(struct ip6_hdr);
1271130365Smlaier
1272130365Smlaier	proto = ip6->ip6_nxt;
1273130365Smlaier	do {
1274130365Smlaier		while (off >= m0->m_len) {
1275130365Smlaier			off -= m0->m_len;
1276130365Smlaier			m0 = m0->m_next;
1277130365Smlaier			if (m0 == NULL)
1278130365Smlaier				return (0);
1279130365Smlaier		}
1280130365Smlaier		if (m0->m_len < off + 4)
1281130365Smlaier			return (0);
1282130365Smlaier
1283130365Smlaier		switch (proto) {
1284130365Smlaier		case IPPROTO_TCP:
1285130365Smlaier		case IPPROTO_UDP: {
1286130365Smlaier			struct udphdr *udp;
1287130365Smlaier
1288130365Smlaier			udp = (struct udphdr *)(mtod(m0, caddr_t) + off);
1289130365Smlaier			fin6->fi6_sport = udp->uh_sport;
1290130365Smlaier			fin6->fi6_dport = udp->uh_dport;
1291130365Smlaier			fin6->fi6_proto = proto;
1292130365Smlaier			}
1293130365Smlaier			return (1);
1294130365Smlaier
1295130365Smlaier		case IPPROTO_ESP:
1296130365Smlaier			if (fin6->fi6_gpi == 0) {
1297130365Smlaier				u_int32_t *gpi;
1298130365Smlaier
1299130365Smlaier				gpi = (u_int32_t *)(mtod(m0, caddr_t) + off);
1300130365Smlaier				fin6->fi6_gpi   = *gpi;
1301130365Smlaier			}
1302130365Smlaier			fin6->fi6_proto = proto;
1303130365Smlaier			return (1);
1304130365Smlaier
1305130365Smlaier		case IPPROTO_AH: {
1306130365Smlaier			/* get next header and header length */
1307130365Smlaier			struct _opt6 *opt6;
1308130365Smlaier
1309130365Smlaier			opt6 = (struct _opt6 *)(mtod(m0, caddr_t) + off);
1310130365Smlaier			if (fin6->fi6_gpi == 0 && m0->m_len >= off + 8)
1311130365Smlaier				fin6->fi6_gpi = opt6->ah_spi;
1312130365Smlaier			proto = opt6->opt6_nxt;
1313130365Smlaier			off += 8 + (opt6->opt6_hlen * 4);
1314130365Smlaier			/* goto the next header */
1315130365Smlaier			break;
1316130365Smlaier			}
1317130365Smlaier
1318130365Smlaier		case IPPROTO_HOPOPTS:
1319130365Smlaier		case IPPROTO_ROUTING:
1320130365Smlaier		case IPPROTO_DSTOPTS: {
1321130365Smlaier			/* get next header and header length */
1322130365Smlaier			struct _opt6 *opt6;
1323130365Smlaier
1324130365Smlaier			opt6 = (struct _opt6 *)(mtod(m0, caddr_t) + off);
1325130365Smlaier			proto = opt6->opt6_nxt;
1326130365Smlaier			off += (opt6->opt6_hlen + 1) * 8;
1327130365Smlaier			/* goto the next header */
1328130365Smlaier			break;
1329130365Smlaier			}
1330130365Smlaier
1331130365Smlaier		case IPPROTO_FRAGMENT:
1332130365Smlaier			/* ipv6 fragmentations are not supported yet */
1333130365Smlaier		default:
1334130365Smlaier			fin6->fi6_proto = proto;
1335130365Smlaier			return (0);
1336130365Smlaier		}
1337130365Smlaier	} while (1);
1338130365Smlaier	/*NOTREACHED*/
1339130365Smlaier}
1340130365Smlaier#endif /* INET6 */
1341130365Smlaier
1342130365Smlaier/*
1343130365Smlaier * altq common classifier
1344130365Smlaier */
1345130365Smlaierint
1346130365Smlaieracc_add_filter(classifier, filter, class, phandle)
1347130365Smlaier	struct acc_classifier *classifier;
1348130365Smlaier	struct flow_filter *filter;
1349130365Smlaier	void	*class;
1350130365Smlaier	u_long	*phandle;
1351130365Smlaier{
1352130365Smlaier	struct acc_filter *afp, *prev, *tmp;
1353130365Smlaier	int	i, s;
1354130365Smlaier
1355130365Smlaier#ifdef INET6
1356130365Smlaier	if (filter->ff_flow.fi_family != AF_INET &&
1357130365Smlaier	    filter->ff_flow.fi_family != AF_INET6)
1358130365Smlaier		return (EINVAL);
1359130365Smlaier#else
1360130365Smlaier	if (filter->ff_flow.fi_family != AF_INET)
1361130365Smlaier		return (EINVAL);
1362130365Smlaier#endif
1363130365Smlaier
1364184205Sdes	afp = malloc(sizeof(struct acc_filter),
1365130365Smlaier	       M_DEVBUF, M_WAITOK);
1366130365Smlaier	if (afp == NULL)
1367130365Smlaier		return (ENOMEM);
1368130365Smlaier	bzero(afp, sizeof(struct acc_filter));
1369130365Smlaier
1370130365Smlaier	afp->f_filter = *filter;
1371130365Smlaier	afp->f_class = class;
1372130365Smlaier
1373130365Smlaier	i = ACC_WILDCARD_INDEX;
1374130365Smlaier	if (filter->ff_flow.fi_family == AF_INET) {
1375130365Smlaier		struct flow_filter *filter4 = &afp->f_filter;
1376130365Smlaier
1377130365Smlaier		/*
1378130365Smlaier		 * if address is 0, it's a wildcard.  if address mask
1379130365Smlaier		 * isn't set, use full mask.
1380130365Smlaier		 */
1381130365Smlaier		if (filter4->ff_flow.fi_dst.s_addr == 0)
1382130365Smlaier			filter4->ff_mask.mask_dst.s_addr = 0;
1383130365Smlaier		else if (filter4->ff_mask.mask_dst.s_addr == 0)
1384130365Smlaier			filter4->ff_mask.mask_dst.s_addr = 0xffffffff;
1385130365Smlaier		if (filter4->ff_flow.fi_src.s_addr == 0)
1386130365Smlaier			filter4->ff_mask.mask_src.s_addr = 0;
1387130365Smlaier		else if (filter4->ff_mask.mask_src.s_addr == 0)
1388130365Smlaier			filter4->ff_mask.mask_src.s_addr = 0xffffffff;
1389130365Smlaier
1390130365Smlaier		/* clear extra bits in addresses  */
1391130365Smlaier		   filter4->ff_flow.fi_dst.s_addr &=
1392130365Smlaier		       filter4->ff_mask.mask_dst.s_addr;
1393130365Smlaier		   filter4->ff_flow.fi_src.s_addr &=
1394130365Smlaier		       filter4->ff_mask.mask_src.s_addr;
1395130365Smlaier
1396130365Smlaier		/*
1397130365Smlaier		 * if dst address is a wildcard, use hash-entry
1398130365Smlaier		 * ACC_WILDCARD_INDEX.
1399130365Smlaier		 */
1400130365Smlaier		if (filter4->ff_mask.mask_dst.s_addr != 0xffffffff)
1401130365Smlaier			i = ACC_WILDCARD_INDEX;
1402130365Smlaier		else
1403130365Smlaier			i = ACC_GET_HASH_INDEX(filter4->ff_flow.fi_dst.s_addr);
1404130365Smlaier	}
1405130365Smlaier#ifdef INET6
1406130365Smlaier	else if (filter->ff_flow.fi_family == AF_INET6) {
1407130365Smlaier		struct flow_filter6 *filter6 =
1408130365Smlaier			(struct flow_filter6 *)&afp->f_filter;
1409130365Smlaier#ifndef IN6MASK0 /* taken from kame ipv6 */
1410130365Smlaier#define	IN6MASK0	{{{ 0, 0, 0, 0 }}}
1411130365Smlaier#define	IN6MASK128	{{{ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }}}
1412130365Smlaier		const struct in6_addr in6mask0 = IN6MASK0;
1413130365Smlaier		const struct in6_addr in6mask128 = IN6MASK128;
1414130365Smlaier#endif
1415130365Smlaier
1416130365Smlaier		if (IN6_IS_ADDR_UNSPECIFIED(&filter6->ff_flow6.fi6_dst))
1417130365Smlaier			filter6->ff_mask6.mask6_dst = in6mask0;
1418130365Smlaier		else if (IN6_IS_ADDR_UNSPECIFIED(&filter6->ff_mask6.mask6_dst))
1419130365Smlaier			filter6->ff_mask6.mask6_dst = in6mask128;
1420130365Smlaier		if (IN6_IS_ADDR_UNSPECIFIED(&filter6->ff_flow6.fi6_src))
1421130365Smlaier			filter6->ff_mask6.mask6_src = in6mask0;
1422130365Smlaier		else if (IN6_IS_ADDR_UNSPECIFIED(&filter6->ff_mask6.mask6_src))
1423130365Smlaier			filter6->ff_mask6.mask6_src = in6mask128;
1424130365Smlaier
1425130365Smlaier		/* clear extra bits in addresses  */
1426130365Smlaier		for (i = 0; i < 16; i++)
1427130365Smlaier			filter6->ff_flow6.fi6_dst.s6_addr[i] &=
1428130365Smlaier			    filter6->ff_mask6.mask6_dst.s6_addr[i];
1429130365Smlaier		for (i = 0; i < 16; i++)
1430130365Smlaier			filter6->ff_flow6.fi6_src.s6_addr[i] &=
1431130365Smlaier			    filter6->ff_mask6.mask6_src.s6_addr[i];
1432130365Smlaier
1433130365Smlaier		if (filter6->ff_flow6.fi6_flowlabel == 0)
1434130365Smlaier			i = ACC_WILDCARD_INDEX;
1435130365Smlaier		else
1436130365Smlaier			i = ACC_GET_HASH_INDEX(filter6->ff_flow6.fi6_flowlabel);
1437130365Smlaier	}
1438130365Smlaier#endif /* INET6 */
1439130365Smlaier
1440130365Smlaier	afp->f_handle = get_filt_handle(classifier, i);
1441130365Smlaier
1442130365Smlaier	/* update filter bitmask */
1443130365Smlaier	afp->f_fbmask = filt2fibmask(filter);
1444130365Smlaier	classifier->acc_fbmask |= afp->f_fbmask;
1445130365Smlaier
1446130365Smlaier	/*
1447130365Smlaier	 * add this filter to the filter list.
1448130365Smlaier	 * filters are ordered from the highest rule number.
1449130365Smlaier	 */
1450130365Smlaier#ifdef __NetBSD__
1451130365Smlaier	s = splnet();
1452130365Smlaier#else
1453130365Smlaier	s = splimp();
1454130365Smlaier#endif
1455130365Smlaier	prev = NULL;
1456130365Smlaier	LIST_FOREACH(tmp, &classifier->acc_filters[i], f_chain) {
1457130365Smlaier		if (tmp->f_filter.ff_ruleno > afp->f_filter.ff_ruleno)
1458130365Smlaier			prev = tmp;
1459130365Smlaier		else
1460130365Smlaier			break;
1461130365Smlaier	}
1462130365Smlaier	if (prev == NULL)
1463130365Smlaier		LIST_INSERT_HEAD(&classifier->acc_filters[i], afp, f_chain);
1464130365Smlaier	else
1465130365Smlaier		LIST_INSERT_AFTER(prev, afp, f_chain);
1466130365Smlaier	splx(s);
1467130365Smlaier
1468130365Smlaier	*phandle = afp->f_handle;
1469130365Smlaier	return (0);
1470130365Smlaier}
1471130365Smlaier
1472130365Smlaierint
1473130365Smlaieracc_delete_filter(classifier, handle)
1474130365Smlaier	struct acc_classifier *classifier;
1475130365Smlaier	u_long handle;
1476130365Smlaier{
1477130365Smlaier	struct acc_filter *afp;
1478130365Smlaier	int	s;
1479130365Smlaier
1480130365Smlaier	if ((afp = filth_to_filtp(classifier, handle)) == NULL)
1481130365Smlaier		return (EINVAL);
1482130365Smlaier
1483130365Smlaier#ifdef __NetBSD__
1484130365Smlaier	s = splnet();
1485130365Smlaier#else
1486130365Smlaier	s = splimp();
1487130365Smlaier#endif
1488130365Smlaier	LIST_REMOVE(afp, f_chain);
1489130365Smlaier	splx(s);
1490130365Smlaier
1491184205Sdes	free(afp, M_DEVBUF);
1492130365Smlaier
1493130365Smlaier	/* todo: update filt_bmask */
1494130365Smlaier
1495130365Smlaier	return (0);
1496130365Smlaier}
1497130365Smlaier
1498130365Smlaier/*
1499130365Smlaier * delete filters referencing to the specified class.
1500130365Smlaier * if the all flag is not 0, delete all the filters.
1501130365Smlaier */
1502130365Smlaierint
1503130365Smlaieracc_discard_filters(classifier, class, all)
1504130365Smlaier	struct acc_classifier *classifier;
1505130365Smlaier	void	*class;
1506130365Smlaier	int	all;
1507130365Smlaier{
1508130365Smlaier	struct acc_filter *afp;
1509130365Smlaier	int	i, s;
1510130365Smlaier
1511130365Smlaier#ifdef __NetBSD__
1512130365Smlaier	s = splnet();
1513130365Smlaier#else
1514130365Smlaier	s = splimp();
1515130365Smlaier#endif
1516130365Smlaier	for (i = 0; i < ACC_FILTER_TABLESIZE; i++) {
1517130365Smlaier		do {
1518130365Smlaier			LIST_FOREACH(afp, &classifier->acc_filters[i], f_chain)
1519130365Smlaier				if (all || afp->f_class == class) {
1520130365Smlaier					LIST_REMOVE(afp, f_chain);
1521184205Sdes					free(afp, M_DEVBUF);
1522130365Smlaier					/* start again from the head */
1523130365Smlaier					break;
1524130365Smlaier				}
1525130365Smlaier		} while (afp != NULL);
1526130365Smlaier	}
1527130365Smlaier	splx(s);
1528130365Smlaier
1529130365Smlaier	if (all)
1530130365Smlaier		classifier->acc_fbmask = 0;
1531130365Smlaier
1532130365Smlaier	return (0);
1533130365Smlaier}
1534130365Smlaier
1535130365Smlaiervoid *
1536130365Smlaieracc_classify(clfier, m, af)
1537130365Smlaier	void *clfier;
1538130365Smlaier	struct mbuf *m;
1539130365Smlaier	int af;
1540130365Smlaier{
1541130365Smlaier	struct acc_classifier *classifier;
1542130365Smlaier	struct flowinfo flow;
1543130365Smlaier	struct acc_filter *afp;
1544130365Smlaier	int	i;
1545130365Smlaier
1546130365Smlaier	classifier = (struct acc_classifier *)clfier;
1547130365Smlaier	altq_extractflow(m, af, &flow, classifier->acc_fbmask);
1548130365Smlaier
1549130365Smlaier	if (flow.fi_family == AF_INET) {
1550130365Smlaier		struct flowinfo_in *fp = (struct flowinfo_in *)&flow;
1551130365Smlaier
1552130365Smlaier		if ((classifier->acc_fbmask & FIMB4_ALL) == FIMB4_TOS) {
1553130365Smlaier			/* only tos is used */
1554130365Smlaier			LIST_FOREACH(afp,
1555130365Smlaier				 &classifier->acc_filters[ACC_WILDCARD_INDEX],
1556130365Smlaier				 f_chain)
1557130365Smlaier				if (apply_tosfilter4(afp->f_fbmask,
1558130365Smlaier						     &afp->f_filter, fp))
1559130365Smlaier					/* filter matched */
1560130365Smlaier					return (afp->f_class);
1561130365Smlaier		} else if ((classifier->acc_fbmask &
1562130365Smlaier			(~(FIMB4_PROTO|FIMB4_SPORT|FIMB4_DPORT) & FIMB4_ALL))
1563130365Smlaier		    == 0) {
1564130365Smlaier			/* only proto and ports are used */
1565130365Smlaier			LIST_FOREACH(afp,
1566130365Smlaier				 &classifier->acc_filters[ACC_WILDCARD_INDEX],
1567130365Smlaier				 f_chain)
1568130365Smlaier				if (apply_ppfilter4(afp->f_fbmask,
1569130365Smlaier						    &afp->f_filter, fp))
1570130365Smlaier					/* filter matched */
1571130365Smlaier					return (afp->f_class);
1572130365Smlaier		} else {
1573130365Smlaier			/* get the filter hash entry from its dest address */
1574130365Smlaier			i = ACC_GET_HASH_INDEX(fp->fi_dst.s_addr);
1575130365Smlaier			do {
1576130365Smlaier				/*
1577130365Smlaier				 * go through this loop twice.  first for dst
1578130365Smlaier				 * hash, second for wildcards.
1579130365Smlaier				 */
1580130365Smlaier				LIST_FOREACH(afp, &classifier->acc_filters[i],
1581130365Smlaier					     f_chain)
1582130365Smlaier					if (apply_filter4(afp->f_fbmask,
1583130365Smlaier							  &afp->f_filter, fp))
1584130365Smlaier						/* filter matched */
1585130365Smlaier						return (afp->f_class);
1586130365Smlaier
1587130365Smlaier				/*
1588130365Smlaier				 * check again for filters with a dst addr
1589130365Smlaier				 * wildcard.
1590130365Smlaier				 * (daddr == 0 || dmask != 0xffffffff).
1591130365Smlaier				 */
1592130365Smlaier				if (i != ACC_WILDCARD_INDEX)
1593130365Smlaier					i = ACC_WILDCARD_INDEX;
1594130365Smlaier				else
1595130365Smlaier					break;
1596130365Smlaier			} while (1);
1597130365Smlaier		}
1598130365Smlaier	}
1599130365Smlaier#ifdef INET6
1600130365Smlaier	else if (flow.fi_family == AF_INET6) {
1601130365Smlaier		struct flowinfo_in6 *fp6 = (struct flowinfo_in6 *)&flow;
1602130365Smlaier
1603130365Smlaier		/* get the filter hash entry from its flow ID */
1604130365Smlaier		if (fp6->fi6_flowlabel != 0)
1605130365Smlaier			i = ACC_GET_HASH_INDEX(fp6->fi6_flowlabel);
1606130365Smlaier		else
1607130365Smlaier			/* flowlable can be zero */
1608130365Smlaier			i = ACC_WILDCARD_INDEX;
1609130365Smlaier
1610130365Smlaier		/* go through this loop twice.  first for flow hash, second
1611130365Smlaier		   for wildcards. */
1612130365Smlaier		do {
1613130365Smlaier			LIST_FOREACH(afp, &classifier->acc_filters[i], f_chain)
1614130365Smlaier				if (apply_filter6(afp->f_fbmask,
1615130365Smlaier					(struct flow_filter6 *)&afp->f_filter,
1616130365Smlaier					fp6))
1617130365Smlaier					/* filter matched */
1618130365Smlaier					return (afp->f_class);
1619130365Smlaier
1620130365Smlaier			/*
1621130365Smlaier			 * check again for filters with a wildcard.
1622130365Smlaier			 */
1623130365Smlaier			if (i != ACC_WILDCARD_INDEX)
1624130365Smlaier				i = ACC_WILDCARD_INDEX;
1625130365Smlaier			else
1626130365Smlaier				break;
1627130365Smlaier		} while (1);
1628130365Smlaier	}
1629130365Smlaier#endif /* INET6 */
1630130365Smlaier
1631130365Smlaier	/* no filter matched */
1632130365Smlaier	return (NULL);
1633130365Smlaier}
1634130365Smlaier
1635130365Smlaierstatic int
1636130365Smlaierapply_filter4(fbmask, filt, pkt)
1637130365Smlaier	u_int32_t	fbmask;
1638130365Smlaier	struct flow_filter *filt;
1639130365Smlaier	struct flowinfo_in *pkt;
1640130365Smlaier{
1641130365Smlaier	if (filt->ff_flow.fi_family != AF_INET)
1642130365Smlaier		return (0);
1643130365Smlaier	if ((fbmask & FIMB4_SPORT) && filt->ff_flow.fi_sport != pkt->fi_sport)
1644130365Smlaier		return (0);
1645130365Smlaier	if ((fbmask & FIMB4_DPORT) && filt->ff_flow.fi_dport != pkt->fi_dport)
1646130365Smlaier		return (0);
1647130365Smlaier	if ((fbmask & FIMB4_DADDR) &&
1648130365Smlaier	    filt->ff_flow.fi_dst.s_addr !=
1649130365Smlaier	    (pkt->fi_dst.s_addr & filt->ff_mask.mask_dst.s_addr))
1650130365Smlaier		return (0);
1651130365Smlaier	if ((fbmask & FIMB4_SADDR) &&
1652130365Smlaier	    filt->ff_flow.fi_src.s_addr !=
1653130365Smlaier	    (pkt->fi_src.s_addr & filt->ff_mask.mask_src.s_addr))
1654130365Smlaier		return (0);
1655130365Smlaier	if ((fbmask & FIMB4_PROTO) && filt->ff_flow.fi_proto != pkt->fi_proto)
1656130365Smlaier		return (0);
1657130365Smlaier	if ((fbmask & FIMB4_TOS) && filt->ff_flow.fi_tos !=
1658130365Smlaier	    (pkt->fi_tos & filt->ff_mask.mask_tos))
1659130365Smlaier		return (0);
1660130365Smlaier	if ((fbmask & FIMB4_GPI) && filt->ff_flow.fi_gpi != (pkt->fi_gpi))
1661130365Smlaier		return (0);
1662130365Smlaier	/* match */
1663130365Smlaier	return (1);
1664130365Smlaier}
1665130365Smlaier
1666130365Smlaier/*
1667130365Smlaier * filter matching function optimized for a common case that checks
1668130365Smlaier * only protocol and port numbers
1669130365Smlaier */
1670130365Smlaierstatic int
1671130365Smlaierapply_ppfilter4(fbmask, filt, pkt)
1672130365Smlaier	u_int32_t	fbmask;
1673130365Smlaier	struct flow_filter *filt;
1674130365Smlaier	struct flowinfo_in *pkt;
1675130365Smlaier{
1676130365Smlaier	if (filt->ff_flow.fi_family != AF_INET)
1677130365Smlaier		return (0);
1678130365Smlaier	if ((fbmask & FIMB4_SPORT) && filt->ff_flow.fi_sport != pkt->fi_sport)
1679130365Smlaier		return (0);
1680130365Smlaier	if ((fbmask & FIMB4_DPORT) && filt->ff_flow.fi_dport != pkt->fi_dport)
1681130365Smlaier		return (0);
1682130365Smlaier	if ((fbmask & FIMB4_PROTO) && filt->ff_flow.fi_proto != pkt->fi_proto)
1683130365Smlaier		return (0);
1684130365Smlaier	/* match */
1685130365Smlaier	return (1);
1686130365Smlaier}
1687130365Smlaier
1688130365Smlaier/*
1689130365Smlaier * filter matching function only for tos field.
1690130365Smlaier */
1691130365Smlaierstatic int
1692130365Smlaierapply_tosfilter4(fbmask, filt, pkt)
1693130365Smlaier	u_int32_t	fbmask;
1694130365Smlaier	struct flow_filter *filt;
1695130365Smlaier	struct flowinfo_in *pkt;
1696130365Smlaier{
1697130365Smlaier	if (filt->ff_flow.fi_family != AF_INET)
1698130365Smlaier		return (0);
1699130365Smlaier	if ((fbmask & FIMB4_TOS) && filt->ff_flow.fi_tos !=
1700130365Smlaier	    (pkt->fi_tos & filt->ff_mask.mask_tos))
1701130365Smlaier		return (0);
1702130365Smlaier	/* match */
1703130365Smlaier	return (1);
1704130365Smlaier}
1705130365Smlaier
1706130365Smlaier#ifdef INET6
1707130365Smlaierstatic int
1708130365Smlaierapply_filter6(fbmask, filt, pkt)
1709130365Smlaier	u_int32_t	fbmask;
1710130365Smlaier	struct flow_filter6 *filt;
1711130365Smlaier	struct flowinfo_in6 *pkt;
1712130365Smlaier{
1713130365Smlaier	int i;
1714130365Smlaier
1715130365Smlaier	if (filt->ff_flow6.fi6_family != AF_INET6)
1716130365Smlaier		return (0);
1717130365Smlaier	if ((fbmask & FIMB6_FLABEL) &&
1718130365Smlaier	    filt->ff_flow6.fi6_flowlabel != pkt->fi6_flowlabel)
1719130365Smlaier		return (0);
1720130365Smlaier	if ((fbmask & FIMB6_PROTO) &&
1721130365Smlaier	    filt->ff_flow6.fi6_proto != pkt->fi6_proto)
1722130365Smlaier		return (0);
1723130365Smlaier	if ((fbmask & FIMB6_SPORT) &&
1724130365Smlaier	    filt->ff_flow6.fi6_sport != pkt->fi6_sport)
1725130365Smlaier		return (0);
1726130365Smlaier	if ((fbmask & FIMB6_DPORT) &&
1727130365Smlaier	    filt->ff_flow6.fi6_dport != pkt->fi6_dport)
1728130365Smlaier		return (0);
1729130365Smlaier	if (fbmask & FIMB6_SADDR) {
1730130365Smlaier		for (i = 0; i < 4; i++)
1731130365Smlaier			if (filt->ff_flow6.fi6_src.s6_addr32[i] !=
1732130365Smlaier			    (pkt->fi6_src.s6_addr32[i] &
1733130365Smlaier			     filt->ff_mask6.mask6_src.s6_addr32[i]))
1734130365Smlaier				return (0);
1735130365Smlaier	}
1736130365Smlaier	if (fbmask & FIMB6_DADDR) {
1737130365Smlaier		for (i = 0; i < 4; i++)
1738130365Smlaier			if (filt->ff_flow6.fi6_dst.s6_addr32[i] !=
1739130365Smlaier			    (pkt->fi6_dst.s6_addr32[i] &
1740130365Smlaier			     filt->ff_mask6.mask6_dst.s6_addr32[i]))
1741130365Smlaier				return (0);
1742130365Smlaier	}
1743130365Smlaier	if ((fbmask & FIMB6_TCLASS) &&
1744130365Smlaier	    filt->ff_flow6.fi6_tclass !=
1745130365Smlaier	    (pkt->fi6_tclass & filt->ff_mask6.mask6_tclass))
1746130365Smlaier		return (0);
1747130365Smlaier	if ((fbmask & FIMB6_GPI) &&
1748130365Smlaier	    filt->ff_flow6.fi6_gpi != pkt->fi6_gpi)
1749130365Smlaier		return (0);
1750130365Smlaier	/* match */
1751130365Smlaier	return (1);
1752130365Smlaier}
1753130365Smlaier#endif /* INET6 */
1754130365Smlaier
1755130365Smlaier/*
1756130365Smlaier *  filter handle:
1757130365Smlaier *	bit 20-28: index to the filter hash table
1758130365Smlaier *	bit  0-19: unique id in the hash bucket.
1759130365Smlaier */
1760130365Smlaierstatic u_long
1761130365Smlaierget_filt_handle(classifier, i)
1762130365Smlaier	struct acc_classifier *classifier;
1763130365Smlaier	int	i;
1764130365Smlaier{
1765130365Smlaier	static u_long handle_number = 1;
1766130365Smlaier	u_long 	handle;
1767130365Smlaier	struct acc_filter *afp;
1768130365Smlaier
1769130365Smlaier	while (1) {
1770130365Smlaier		handle = handle_number++ & 0x000fffff;
1771130365Smlaier
1772130365Smlaier		if (LIST_EMPTY(&classifier->acc_filters[i]))
1773130365Smlaier			break;
1774130365Smlaier
1775130365Smlaier		LIST_FOREACH(afp, &classifier->acc_filters[i], f_chain)
1776130365Smlaier			if ((afp->f_handle & 0x000fffff) == handle)
1777130365Smlaier				break;
1778130365Smlaier		if (afp == NULL)
1779130365Smlaier			break;
1780130365Smlaier		/* this handle is already used, try again */
1781130365Smlaier	}
1782130365Smlaier
1783130365Smlaier	return ((i << 20) | handle);
1784130365Smlaier}
1785130365Smlaier
1786130365Smlaier/* convert filter handle to filter pointer */
1787130365Smlaierstatic struct acc_filter *
1788130365Smlaierfilth_to_filtp(classifier, handle)
1789130365Smlaier	struct acc_classifier *classifier;
1790130365Smlaier	u_long handle;
1791130365Smlaier{
1792130365Smlaier	struct acc_filter *afp;
1793130365Smlaier	int	i;
1794130365Smlaier
1795130365Smlaier	i = ACC_GET_HINDEX(handle);
1796130365Smlaier
1797130365Smlaier	LIST_FOREACH(afp, &classifier->acc_filters[i], f_chain)
1798130365Smlaier		if (afp->f_handle == handle)
1799130365Smlaier			return (afp);
1800130365Smlaier
1801130365Smlaier	return (NULL);
1802130365Smlaier}
1803130365Smlaier
1804130365Smlaier/* create flowinfo bitmask */
1805130365Smlaierstatic u_int32_t
1806130365Smlaierfilt2fibmask(filt)
1807130365Smlaier	struct flow_filter *filt;
1808130365Smlaier{
1809130365Smlaier	u_int32_t mask = 0;
1810130365Smlaier#ifdef INET6
1811130365Smlaier	struct flow_filter6 *filt6;
1812130365Smlaier#endif
1813130365Smlaier
1814130365Smlaier	switch (filt->ff_flow.fi_family) {
1815130365Smlaier	case AF_INET:
1816130365Smlaier		if (filt->ff_flow.fi_proto != 0)
1817130365Smlaier			mask |= FIMB4_PROTO;
1818130365Smlaier		if (filt->ff_flow.fi_tos != 0)
1819130365Smlaier			mask |= FIMB4_TOS;
1820130365Smlaier		if (filt->ff_flow.fi_dst.s_addr != 0)
1821130365Smlaier			mask |= FIMB4_DADDR;
1822130365Smlaier		if (filt->ff_flow.fi_src.s_addr != 0)
1823130365Smlaier			mask |= FIMB4_SADDR;
1824130365Smlaier		if (filt->ff_flow.fi_sport != 0)
1825130365Smlaier			mask |= FIMB4_SPORT;
1826130365Smlaier		if (filt->ff_flow.fi_dport != 0)
1827130365Smlaier			mask |= FIMB4_DPORT;
1828130365Smlaier		if (filt->ff_flow.fi_gpi != 0)
1829130365Smlaier			mask |= FIMB4_GPI;
1830130365Smlaier		break;
1831130365Smlaier#ifdef INET6
1832130365Smlaier	case AF_INET6:
1833130365Smlaier		filt6 = (struct flow_filter6 *)filt;
1834130365Smlaier
1835130365Smlaier		if (filt6->ff_flow6.fi6_proto != 0)
1836130365Smlaier			mask |= FIMB6_PROTO;
1837130365Smlaier		if (filt6->ff_flow6.fi6_tclass != 0)
1838130365Smlaier			mask |= FIMB6_TCLASS;
1839130365Smlaier		if (!IN6_IS_ADDR_UNSPECIFIED(&filt6->ff_flow6.fi6_dst))
1840130365Smlaier			mask |= FIMB6_DADDR;
1841130365Smlaier		if (!IN6_IS_ADDR_UNSPECIFIED(&filt6->ff_flow6.fi6_src))
1842130365Smlaier			mask |= FIMB6_SADDR;
1843130365Smlaier		if (filt6->ff_flow6.fi6_sport != 0)
1844130365Smlaier			mask |= FIMB6_SPORT;
1845130365Smlaier		if (filt6->ff_flow6.fi6_dport != 0)
1846130365Smlaier			mask |= FIMB6_DPORT;
1847130365Smlaier		if (filt6->ff_flow6.fi6_gpi != 0)
1848130365Smlaier			mask |= FIMB6_GPI;
1849130365Smlaier		if (filt6->ff_flow6.fi6_flowlabel != 0)
1850130365Smlaier			mask |= FIMB6_FLABEL;
1851130365Smlaier		break;
1852130365Smlaier#endif /* INET6 */
1853130365Smlaier	}
1854130365Smlaier	return (mask);
1855130365Smlaier}
1856130365Smlaier
1857130365Smlaier
1858130365Smlaier/*
1859130365Smlaier * helper functions to handle IPv4 fragments.
1860130365Smlaier * currently only in-sequence fragments are handled.
1861130365Smlaier *	- fragment info is cached in a LRU list.
1862130365Smlaier *	- when a first fragment is found, cache its flow info.
1863130365Smlaier *	- when a non-first fragment is found, lookup the cache.
1864130365Smlaier */
1865130365Smlaier
1866130365Smlaierstruct ip4_frag {
1867130365Smlaier    TAILQ_ENTRY(ip4_frag) ip4f_chain;
1868130365Smlaier    char    ip4f_valid;
1869130365Smlaier    u_short ip4f_id;
1870130365Smlaier    struct flowinfo_in ip4f_info;
1871130365Smlaier};
1872130365Smlaier
1873130365Smlaierstatic TAILQ_HEAD(ip4f_list, ip4_frag) ip4f_list; /* IPv4 fragment cache */
1874130365Smlaier
1875130365Smlaier#define	IP4F_TABSIZE		16	/* IPv4 fragment cache size */
1876130365Smlaier
1877130365Smlaier
1878130365Smlaierstatic void
1879130365Smlaierip4f_cache(ip, fin)
1880130365Smlaier	struct ip *ip;
1881130365Smlaier	struct flowinfo_in *fin;
1882130365Smlaier{
1883130365Smlaier	struct ip4_frag *fp;
1884130365Smlaier
1885130365Smlaier	if (TAILQ_EMPTY(&ip4f_list)) {
1886130365Smlaier		/* first time call, allocate fragment cache entries. */
1887130365Smlaier		if (ip4f_init() < 0)
1888130365Smlaier			/* allocation failed! */
1889130365Smlaier			return;
1890130365Smlaier	}
1891130365Smlaier
1892130365Smlaier	fp = ip4f_alloc();
1893130365Smlaier	fp->ip4f_id = ip->ip_id;
1894130365Smlaier	fp->ip4f_info.fi_proto = ip->ip_p;
1895130365Smlaier	fp->ip4f_info.fi_src.s_addr = ip->ip_src.s_addr;
1896130365Smlaier	fp->ip4f_info.fi_dst.s_addr = ip->ip_dst.s_addr;
1897130365Smlaier
1898130365Smlaier	/* save port numbers */
1899130365Smlaier	fp->ip4f_info.fi_sport = fin->fi_sport;
1900130365Smlaier	fp->ip4f_info.fi_dport = fin->fi_dport;
1901130365Smlaier	fp->ip4f_info.fi_gpi   = fin->fi_gpi;
1902130365Smlaier}
1903130365Smlaier
1904130365Smlaierstatic int
1905130365Smlaierip4f_lookup(ip, fin)
1906130365Smlaier	struct ip *ip;
1907130365Smlaier	struct flowinfo_in *fin;
1908130365Smlaier{
1909130365Smlaier	struct ip4_frag *fp;
1910130365Smlaier
1911130365Smlaier	for (fp = TAILQ_FIRST(&ip4f_list); fp != NULL && fp->ip4f_valid;
1912130365Smlaier	     fp = TAILQ_NEXT(fp, ip4f_chain))
1913130365Smlaier		if (ip->ip_id == fp->ip4f_id &&
1914130365Smlaier		    ip->ip_src.s_addr == fp->ip4f_info.fi_src.s_addr &&
1915130365Smlaier		    ip->ip_dst.s_addr == fp->ip4f_info.fi_dst.s_addr &&
1916130365Smlaier		    ip->ip_p == fp->ip4f_info.fi_proto) {
1917130365Smlaier
1918130365Smlaier			/* found the matching entry */
1919130365Smlaier			fin->fi_sport = fp->ip4f_info.fi_sport;
1920130365Smlaier			fin->fi_dport = fp->ip4f_info.fi_dport;
1921130365Smlaier			fin->fi_gpi   = fp->ip4f_info.fi_gpi;
1922130365Smlaier
1923130365Smlaier			if ((ntohs(ip->ip_off) & IP_MF) == 0)
1924130365Smlaier				/* this is the last fragment,
1925130365Smlaier				   release the entry. */
1926130365Smlaier				ip4f_free(fp);
1927130365Smlaier
1928130365Smlaier			return (1);
1929130365Smlaier		}
1930130365Smlaier
1931130365Smlaier	/* no matching entry found */
1932130365Smlaier	return (0);
1933130365Smlaier}
1934130365Smlaier
1935130365Smlaierstatic int
1936130365Smlaierip4f_init(void)
1937130365Smlaier{
1938130365Smlaier	struct ip4_frag *fp;
1939130365Smlaier	int i;
1940130365Smlaier
1941130365Smlaier	TAILQ_INIT(&ip4f_list);
1942130365Smlaier	for (i=0; i<IP4F_TABSIZE; i++) {
1943184205Sdes		fp = malloc(sizeof(struct ip4_frag),
1944130365Smlaier		       M_DEVBUF, M_NOWAIT);
1945130365Smlaier		if (fp == NULL) {
1946130365Smlaier			printf("ip4f_init: can't alloc %dth entry!\n", i);
1947130365Smlaier			if (i == 0)
1948130365Smlaier				return (-1);
1949130365Smlaier			return (0);
1950130365Smlaier		}
1951130365Smlaier		fp->ip4f_valid = 0;
1952130365Smlaier		TAILQ_INSERT_TAIL(&ip4f_list, fp, ip4f_chain);
1953130365Smlaier	}
1954130365Smlaier	return (0);
1955130365Smlaier}
1956130365Smlaier
1957130365Smlaierstatic struct ip4_frag *
1958130365Smlaierip4f_alloc(void)
1959130365Smlaier{
1960130365Smlaier	struct ip4_frag *fp;
1961130365Smlaier
1962130365Smlaier	/* reclaim an entry at the tail, put it at the head */
1963130365Smlaier	fp = TAILQ_LAST(&ip4f_list, ip4f_list);
1964130365Smlaier	TAILQ_REMOVE(&ip4f_list, fp, ip4f_chain);
1965130365Smlaier	fp->ip4f_valid = 1;
1966130365Smlaier	TAILQ_INSERT_HEAD(&ip4f_list, fp, ip4f_chain);
1967130365Smlaier	return (fp);
1968130365Smlaier}
1969130365Smlaier
1970130365Smlaierstatic void
1971130365Smlaierip4f_free(fp)
1972130365Smlaier	struct ip4_frag *fp;
1973130365Smlaier{
1974130365Smlaier	TAILQ_REMOVE(&ip4f_list, fp, ip4f_chain);
1975130365Smlaier	fp->ip4f_valid = 0;
1976130365Smlaier	TAILQ_INSERT_TAIL(&ip4f_list, fp, ip4f_chain);
1977130365Smlaier}
1978130365Smlaier
1979130365Smlaier#endif /* ALTQ3_CLFIER_COMPAT */
1980