ip_htable.c revision 147547
1/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_htable.c 147547 2005-06-23 14:19:02Z darrenr $	*/
2
3/*
4 * Copyright (C) 1993-2001, 2003 by Darren Reed.
5 *
6 * See the IPFILTER.LICENCE file for details on licencing.
7 */
8#if defined(KERNEL) || defined(_KERNEL)
9# undef KERNEL
10# undef _KERNEL
11# define        KERNEL	1
12# define        _KERNEL	1
13#endif
14#include <sys/param.h>
15#include <sys/types.h>
16#include <sys/errno.h>
17#include <sys/time.h>
18#include <sys/file.h>
19#if !defined(_KERNEL)
20# include <stdlib.h>
21# include <string.h>
22# define _KERNEL
23# ifdef __OpenBSD__
24struct file;
25# endif
26# include <sys/uio.h>
27# undef _KERNEL
28#endif
29#include <sys/socket.h>
30#if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
31# include <sys/malloc.h>
32#endif
33#if defined(__FreeBSD__)
34#  include <sys/cdefs.h>
35#  include <sys/proc.h>
36#endif
37#if !defined(__svr4__) && !defined(__SVR4) && !defined(__hpux) && \
38    !defined(linux)
39# include <sys/mbuf.h>
40#endif
41#if defined(_KERNEL)
42# include <sys/systm.h>
43#else
44# include <stdio.h>
45#endif
46#include <netinet/in.h>
47#include <net/if.h>
48
49#include "netinet/ip_compat.h"
50#include "netinet/ip_fil.h"
51#include "netinet/ip_lookup.h"
52#include "netinet/ip_htable.h"
53/* END OF INCLUDES */
54
55#if !defined(lint)
56static const char rcsid[] = "@(#)Id: ip_htable.c,v 2.34.2.2 2004/10/17 15:49:15 darrenr Exp";
57#endif
58
59#ifdef	IPFILTER_LOOKUP
60static iphtent_t *fr_iphmfind __P((iphtable_t *, struct in_addr *));
61static	u_long	ipht_nomem[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 };
62static	u_long	ipf_nhtables[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 };
63static	u_long	ipf_nhtnodes[IPL_LOGSIZE] = { 0, 0, 0, 0, 0, 0, 0, 0 };
64
65iphtable_t *ipf_htables[IPL_LOGSIZE] = { NULL, NULL, NULL, NULL,
66					 NULL, NULL, NULL, NULL };
67
68
69void fr_htable_unload()
70{
71	iplookupflush_t fop;
72
73	fop.iplf_unit = IPL_LOGALL;
74	(void)fr_flushhtable(&fop);
75}
76
77
78int fr_gethtablestat(op)
79iplookupop_t *op;
80{
81	iphtstat_t stats;
82
83	if (op->iplo_size != sizeof(stats))
84		return EINVAL;
85
86	stats.iphs_tables = ipf_htables[op->iplo_unit];
87	stats.iphs_numtables = ipf_nhtables[op->iplo_unit];
88	stats.iphs_numnodes = ipf_nhtnodes[op->iplo_unit];
89	stats.iphs_nomem = ipht_nomem[op->iplo_unit];
90
91	return COPYOUT(&stats, op->iplo_struct, sizeof(stats));
92
93}
94
95
96/*
97 * Create a new hash table using the template passed.
98 */
99int fr_newhtable(op)
100iplookupop_t *op;
101{
102	iphtable_t *iph, *oiph;
103	char name[FR_GROUPLEN];
104	int err, i, unit;
105
106	KMALLOC(iph, iphtable_t *);
107	if (iph == NULL) {
108		ipht_nomem[op->iplo_unit]++;
109		return ENOMEM;
110	}
111
112	err = COPYIN(op->iplo_struct, iph, sizeof(*iph));
113	if (err != 0) {
114		KFREE(iph);
115		return EFAULT;
116	}
117
118	unit = op->iplo_unit;
119	if (iph->iph_unit != unit) {
120		KFREE(iph);
121		return EINVAL;
122	}
123
124	if ((op->iplo_arg & IPHASH_ANON) == 0) {
125		if (fr_findhtable(op->iplo_unit, op->iplo_name) != NULL) {
126			KFREE(iph);
127			return EEXIST;
128		}
129	} else {
130		i = IPHASH_ANON;
131		do {
132			i++;
133#if defined(SNPRINTF) && defined(_KERNEL)
134			SNPRINTF(name, sizeof(name), "%u", i);
135#else
136			(void)sprintf(name, "%u", i);
137#endif
138			for (oiph = ipf_htables[unit]; oiph != NULL;
139			     oiph = oiph->iph_next)
140				if (strncmp(oiph->iph_name, name,
141					    sizeof(oiph->iph_name)) == 0)
142					break;
143		} while (oiph != NULL);
144		(void)strncpy(iph->iph_name, name, sizeof(iph->iph_name));
145		err = COPYOUT(iph, op->iplo_struct, sizeof(*iph));
146		if (err != 0) {
147			KFREE(iph);
148			return EFAULT;
149		}
150		iph->iph_type |= IPHASH_ANON;
151	}
152
153	KMALLOCS(iph->iph_table, iphtent_t **,
154		 iph->iph_size * sizeof(*iph->iph_table));
155	if (iph->iph_table == NULL) {
156		KFREE(iph);
157		ipht_nomem[unit]++;
158		return ENOMEM;
159	}
160
161	bzero((char *)iph->iph_table, iph->iph_size * sizeof(*iph->iph_table));
162	iph->iph_masks = 0;
163
164	iph->iph_next = ipf_htables[unit];
165	iph->iph_pnext = &ipf_htables[unit];
166	if (ipf_htables[unit] != NULL)
167		ipf_htables[unit]->iph_pnext = &iph->iph_next;
168	ipf_htables[unit] = iph;
169
170	ipf_nhtables[unit]++;
171
172	return 0;
173}
174
175
176/*
177 */
178int fr_removehtable(op)
179iplookupop_t *op;
180{
181	iphtable_t *iph;
182
183
184	iph = fr_findhtable(op->iplo_unit, op->iplo_name);
185	if (iph == NULL)
186		return ESRCH;
187
188	if (iph->iph_unit != op->iplo_unit) {
189		return EINVAL;
190	}
191
192	if (iph->iph_ref != 0) {
193		return EBUSY;
194	}
195
196	fr_delhtable(iph);
197
198	return 0;
199}
200
201
202void fr_delhtable(iph)
203iphtable_t *iph;
204{
205	iphtent_t *ipe;
206	int i;
207
208	for (i = 0; i < iph->iph_size; i++)
209		while ((ipe = iph->iph_table[i]) != NULL)
210			if (fr_delhtent(iph, ipe) != 0)
211				return;
212
213	*iph->iph_pnext = iph->iph_next;
214	if (iph->iph_next != NULL)
215		iph->iph_next->iph_pnext = iph->iph_pnext;
216
217	ipf_nhtables[iph->iph_unit]--;
218
219	if (iph->iph_ref == 0) {
220		KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table));
221		KFREE(iph);
222	}
223}
224
225
226void fr_derefhtable(iph)
227iphtable_t *iph;
228{
229	iph->iph_ref--;
230	if (iph->iph_ref == 0)
231		fr_delhtable(iph);
232}
233
234
235iphtable_t *fr_findhtable(unit, name)
236int unit;
237char *name;
238{
239	iphtable_t *iph;
240
241	for (iph = ipf_htables[unit]; iph != NULL; iph = iph->iph_next)
242		if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0)
243			break;
244	return iph;
245}
246
247
248size_t fr_flushhtable(op)
249iplookupflush_t *op;
250{
251	iphtable_t *iph;
252	size_t freed;
253	int i;
254
255	freed = 0;
256
257	for (i = 0; i <= IPL_LOGMAX; i++) {
258		if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) {
259			while ((iph = ipf_htables[i]) != NULL) {
260				fr_delhtable(iph);
261				freed++;
262			}
263		}
264	}
265
266	return freed;
267}
268
269
270/*
271 * Add an entry to a hash table.
272 */
273int fr_addhtent(iph, ipeo)
274iphtable_t *iph;
275iphtent_t *ipeo;
276{
277	iphtent_t *ipe;
278	u_int hv;
279	int bits;
280
281	KMALLOC(ipe, iphtent_t *);
282	if (ipe == NULL)
283		return -1;
284
285	bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe));
286	ipe->ipe_addr.in4_addr &= ipe->ipe_mask.in4_addr;
287	ipe->ipe_addr.in4_addr = ntohl(ipe->ipe_addr.in4_addr);
288	bits = count4bits(ipe->ipe_mask.in4_addr);
289	ipe->ipe_mask.in4_addr = ntohl(ipe->ipe_mask.in4_addr);
290
291	hv = IPE_HASH_FN(ipe->ipe_addr.in4_addr, ipe->ipe_mask.in4_addr,
292			 iph->iph_size);
293	ipe->ipe_ref = 0;
294	ipe->ipe_next = iph->iph_table[hv];
295	ipe->ipe_pnext = iph->iph_table + hv;
296
297	if (iph->iph_table[hv] != NULL)
298		iph->iph_table[hv]->ipe_pnext = &ipe->ipe_next;
299	iph->iph_table[hv] = ipe;
300	if ((bits >= 0) && (bits != 32))
301		iph->iph_masks |= 1 << bits;
302
303	switch (iph->iph_type & ~IPHASH_ANON)
304	{
305	case IPHASH_GROUPMAP :
306		ipe->ipe_ptr = fr_addgroup(ipe->ipe_group, NULL,
307					   iph->iph_flags, IPL_LOGIPF,
308					   fr_active);
309		break;
310
311	default :
312		ipe->ipe_ptr = NULL;
313		ipe->ipe_value = 0;
314		break;
315	}
316
317	ipf_nhtnodes[iph->iph_unit]++;
318
319	return 0;
320}
321
322
323/*
324 * Delete an entry from a hash table.
325 */
326int fr_delhtent(iph, ipe)
327iphtable_t *iph;
328iphtent_t *ipe;
329{
330
331	if (ipe->ipe_ref != 0)
332		return EBUSY;
333
334
335	*ipe->ipe_pnext = ipe->ipe_next;
336	if (ipe->ipe_next != NULL)
337		ipe->ipe_next->ipe_pnext = ipe->ipe_pnext;
338
339	switch (iph->iph_type & ~IPHASH_ANON)
340	{
341	case IPHASH_GROUPMAP :
342		if (ipe->ipe_group != NULL)
343			fr_delgroup(ipe->ipe_group, IPL_LOGIPF, fr_active);
344		break;
345
346	default :
347		ipe->ipe_ptr = NULL;
348		ipe->ipe_value = 0;
349		break;
350	}
351
352	KFREE(ipe);
353
354	ipf_nhtnodes[iph->iph_unit]--;
355
356	return 0;
357}
358
359
360void *fr_iphmfindgroup(tptr, aptr)
361void *tptr, *aptr;
362{
363	struct in_addr *addr;
364	iphtable_t *iph;
365	iphtent_t *ipe;
366	void *rval;
367
368	READ_ENTER(&ip_poolrw);
369	iph = tptr;
370	addr = aptr;
371
372	ipe = fr_iphmfind(iph, addr);
373	if (ipe != NULL)
374		rval = ipe->ipe_ptr;
375	else
376		rval = NULL;
377	RWLOCK_EXIT(&ip_poolrw);
378	return rval;
379}
380
381
382/* ------------------------------------------------------------------------ */
383/* Function:    fr_iphmfindip                                               */
384/* Returns:     int     - 0 == +ve match, -1 == error, 1 == -ve/no match    */
385/* Parameters:  tptr(I)    - pointer to the pool to search                  */
386/*              version(I) - IP protocol version (4 or 6)                   */
387/*              aptr(I)    - pointer to address information                 */
388/*                                                                          */
389/* Search the hash table for a given address and return a search result.    */
390/* ------------------------------------------------------------------------ */
391int fr_iphmfindip(tptr, version, aptr)
392void *tptr, *aptr;
393int version;
394{
395	struct in_addr *addr;
396	iphtable_t *iph;
397	iphtent_t *ipe;
398	int rval;
399
400	if (version != 4)
401		return -1;
402
403	if (tptr == NULL || aptr == NULL)
404		return -1;
405
406	iph = tptr;
407	addr = aptr;
408
409	READ_ENTER(&ip_poolrw);
410	ipe = fr_iphmfind(iph, addr);
411	if (ipe != NULL)
412		rval = 0;
413	else
414		rval = 1;
415	RWLOCK_EXIT(&ip_poolrw);
416	return rval;
417}
418
419
420/* Locks:  ip_poolrw */
421static iphtent_t *fr_iphmfind(iph, addr)
422iphtable_t *iph;
423struct in_addr *addr;
424{
425	u_32_t hmsk, msk, ips;
426	iphtent_t *ipe;
427	u_int hv;
428
429	hmsk = iph->iph_masks;
430	msk = 0xffffffff;
431maskloop:
432	ips = ntohl(addr->s_addr) & msk;
433	hv = IPE_HASH_FN(ips, msk, iph->iph_size);
434	for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_next) {
435		if (ipe->ipe_mask.in4_addr != msk ||
436		    ipe->ipe_addr.in4_addr != ips) {
437			continue;
438		}
439		break;
440	}
441
442	if ((ipe == NULL) && (hmsk != 0)) {
443		while (hmsk != 0) {
444			msk <<= 1;
445			if (hmsk & 0x80000000)
446				break;
447			hmsk <<= 1;
448		}
449		if (hmsk != 0) {
450			hmsk <<= 1;
451			goto maskloop;
452		}
453	}
454	return ipe;
455}
456
457#endif /* IPFILTER_LOOKUP */
458