ip_htable.c revision 145517
1/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_htable.c 145517 2005-04-25 18:15:41Z 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		return ENOMEM;
109
110	err = COPYIN(op->iplo_struct, iph, sizeof(*iph));
111	if (err != 0) {
112		KFREE(iph);
113		return EFAULT;
114	}
115
116	unit = op->iplo_unit;
117	if (iph->iph_unit != unit) {
118		KFREE(iph);
119		return EINVAL;
120	}
121
122	if ((op->iplo_arg & IPHASH_ANON) == 0) {
123		if (fr_findhtable(op->iplo_unit, op->iplo_name) != NULL) {
124			KFREE(iph);
125			return EEXIST;
126		}
127	} else {
128		i = IPHASH_ANON;
129		do {
130			i++;
131#if defined(SNPRINTF) && defined(_KERNEL)
132			SNPRINTF(name, sizeof(name), "%u", i);
133#else
134			(void)sprintf(name, "%u", i);
135#endif
136			for (oiph = ipf_htables[unit]; oiph != NULL;
137			     oiph = oiph->iph_next)
138				if (strncmp(oiph->iph_name, name,
139					    sizeof(oiph->iph_name)) == 0)
140					break;
141		} while (oiph != NULL);
142		(void)strncpy(iph->iph_name, name, sizeof(iph->iph_name));
143		err = COPYOUT(iph, op->iplo_struct, sizeof(*iph));
144		if (err != 0) {
145			KFREE(iph);
146			return EFAULT;
147		}
148		iph->iph_type |= IPHASH_ANON;
149	}
150
151	KMALLOCS(iph->iph_table, iphtent_t **,
152		 iph->iph_size * sizeof(*iph->iph_table));
153	if (iph->iph_table == NULL) {
154		KFREE(iph);
155		ipht_nomem[unit]++;
156		return ENOMEM;
157	}
158
159	bzero((char *)iph->iph_table, iph->iph_size * sizeof(*iph->iph_table));
160	iph->iph_masks = 0;
161
162	iph->iph_next = ipf_htables[unit];
163	iph->iph_pnext = &ipf_htables[unit];
164	if (ipf_htables[unit] != NULL)
165		ipf_htables[unit]->iph_pnext = &iph->iph_next;
166	ipf_htables[unit] = iph;
167
168	ipf_nhtables[unit]++;
169
170	return 0;
171}
172
173
174/*
175 */
176int fr_removehtable(op)
177iplookupop_t *op;
178{
179	iphtable_t *iph;
180
181
182	iph = fr_findhtable(op->iplo_unit, op->iplo_name);
183	if (iph == NULL)
184		return ESRCH;
185
186	if (iph->iph_unit != op->iplo_unit) {
187		return EINVAL;
188	}
189
190	if (iph->iph_ref != 0) {
191		return EBUSY;
192	}
193
194	fr_delhtable(iph);
195
196	return 0;
197}
198
199
200void fr_delhtable(iph)
201iphtable_t *iph;
202{
203	iphtent_t *ipe;
204	int i;
205
206	for (i = 0; i < iph->iph_size; i++)
207		while ((ipe = iph->iph_table[i]) != NULL)
208			if (fr_delhtent(iph, ipe) != 0)
209				return;
210
211	*iph->iph_pnext = iph->iph_next;
212	if (iph->iph_next != NULL)
213		iph->iph_next->iph_pnext = iph->iph_pnext;
214
215	ipf_nhtables[iph->iph_unit]--;
216
217	if (iph->iph_ref == 0) {
218		KFREES(iph->iph_table, iph->iph_size * sizeof(*iph->iph_table));
219		KFREE(iph);
220	}
221}
222
223
224void fr_derefhtable(iph)
225iphtable_t *iph;
226{
227	iph->iph_ref--;
228	if (iph->iph_ref == 0)
229		fr_delhtable(iph);
230}
231
232
233iphtable_t *fr_findhtable(unit, name)
234int unit;
235char *name;
236{
237	iphtable_t *iph;
238
239	for (iph = ipf_htables[unit]; iph != NULL; iph = iph->iph_next)
240		if (strncmp(iph->iph_name, name, sizeof(iph->iph_name)) == 0)
241			break;
242	return iph;
243}
244
245
246size_t fr_flushhtable(op)
247iplookupflush_t *op;
248{
249	iphtable_t *iph;
250	size_t freed;
251	int i;
252
253	freed = 0;
254
255	for (i = 0; i <= IPL_LOGMAX; i++) {
256		if (op->iplf_unit == i || op->iplf_unit == IPL_LOGALL) {
257			while ((iph = ipf_htables[i]) != NULL) {
258				fr_delhtable(iph);
259				freed++;
260			}
261		}
262	}
263
264	return freed;
265}
266
267
268/*
269 * Add an entry to a hash table.
270 */
271int fr_addhtent(iph, ipeo)
272iphtable_t *iph;
273iphtent_t *ipeo;
274{
275	iphtent_t *ipe;
276	u_int hv;
277	int bits;
278
279	KMALLOC(ipe, iphtent_t *);
280	if (ipe == NULL)
281		return -1;
282
283	bcopy((char *)ipeo, (char *)ipe, sizeof(*ipe));
284	ipe->ipe_addr.in4_addr &= ipe->ipe_mask.in4_addr;
285	ipe->ipe_addr.in4_addr = ntohl(ipe->ipe_addr.in4_addr);
286	bits = count4bits(ipe->ipe_mask.in4_addr);
287	ipe->ipe_mask.in4_addr = ntohl(ipe->ipe_mask.in4_addr);
288
289	hv = IPE_HASH_FN(ipe->ipe_addr.in4_addr, ipe->ipe_mask.in4_addr,
290			 iph->iph_size);
291	ipe->ipe_ref = 0;
292	ipe->ipe_next = iph->iph_table[hv];
293	ipe->ipe_pnext = iph->iph_table + hv;
294
295	if (iph->iph_table[hv] != NULL)
296		iph->iph_table[hv]->ipe_pnext = &ipe->ipe_next;
297	iph->iph_table[hv] = ipe;
298	if ((bits >= 0) && (bits != 32))
299		iph->iph_masks |= 1 << bits;
300
301	switch (iph->iph_type & ~IPHASH_ANON)
302	{
303	case IPHASH_GROUPMAP :
304		ipe->ipe_ptr = fr_addgroup(ipe->ipe_group, NULL,
305					   iph->iph_flags, IPL_LOGIPF,
306					   fr_active);
307		break;
308
309	default :
310		ipe->ipe_ptr = NULL;
311		ipe->ipe_value = 0;
312		break;
313	}
314
315	ipf_nhtnodes[iph->iph_unit]++;
316
317	return 0;
318}
319
320
321/*
322 * Delete an entry from a hash table.
323 */
324int fr_delhtent(iph, ipe)
325iphtable_t *iph;
326iphtent_t *ipe;
327{
328
329	if (ipe->ipe_ref != 0)
330		return EBUSY;
331
332
333	*ipe->ipe_pnext = ipe->ipe_next;
334	if (ipe->ipe_next != NULL)
335		ipe->ipe_next->ipe_pnext = ipe->ipe_pnext;
336
337	switch (iph->iph_type & ~IPHASH_ANON)
338	{
339	case IPHASH_GROUPMAP :
340		if (ipe->ipe_group != NULL)
341			fr_delgroup(ipe->ipe_group, IPL_LOGIPF, fr_active);
342		break;
343
344	default :
345		ipe->ipe_ptr = NULL;
346		ipe->ipe_value = 0;
347		break;
348	}
349
350	KFREE(ipe);
351
352	ipf_nhtnodes[iph->iph_unit]--;
353
354	return 0;
355}
356
357
358void *fr_iphmfindgroup(tptr, aptr)
359void *tptr, *aptr;
360{
361	struct in_addr *addr;
362	iphtable_t *iph;
363	iphtent_t *ipe;
364	void *rval;
365
366	READ_ENTER(&ip_poolrw);
367	iph = tptr;
368	addr = aptr;
369
370	ipe = fr_iphmfind(iph, addr);
371	if (ipe != NULL)
372		rval = ipe->ipe_ptr;
373	else
374		rval = NULL;
375	RWLOCK_EXIT(&ip_poolrw);
376	return rval;
377}
378
379
380/* ------------------------------------------------------------------------ */
381/* Function:    fr_iphmfindip                                               */
382/* Returns:     int     - 0 == +ve match, -1 == error, 1 == -ve/no match    */
383/* Parameters:  tptr(I)    - pointer to the pool to search                  */
384/*              version(I) - IP protocol version (4 or 6)                   */
385/*              aptr(I)    - pointer to address information                 */
386/*                                                                          */
387/* Search the hash table for a given address and return a search result.    */
388/* ------------------------------------------------------------------------ */
389int fr_iphmfindip(tptr, version, aptr)
390void *tptr, *aptr;
391int version;
392{
393	struct in_addr *addr;
394	iphtable_t *iph;
395	iphtent_t *ipe;
396	int rval;
397
398	if (version != 4)
399		return -1;
400
401	if (tptr == NULL || aptr == NULL)
402		return -1;
403
404	iph = tptr;
405	addr = aptr;
406
407	READ_ENTER(&ip_poolrw);
408	ipe = fr_iphmfind(iph, addr);
409	if (ipe != NULL)
410		rval = 0;
411	else
412		rval = 1;
413	RWLOCK_EXIT(&ip_poolrw);
414	return rval;
415}
416
417
418/* Locks:  ip_poolrw */
419static iphtent_t *fr_iphmfind(iph, addr)
420iphtable_t *iph;
421struct in_addr *addr;
422{
423	u_32_t hmsk, msk, ips;
424	iphtent_t *ipe;
425	u_int hv;
426
427	hmsk = iph->iph_masks;
428	msk = 0xffffffff;
429maskloop:
430	ips = ntohl(addr->s_addr) & msk;
431	hv = IPE_HASH_FN(ips, msk, iph->iph_size);
432	for (ipe = iph->iph_table[hv]; (ipe != NULL); ipe = ipe->ipe_next) {
433		if (ipe->ipe_mask.in4_addr != msk ||
434		    ipe->ipe_addr.in4_addr != ips) {
435			continue;
436		}
437		break;
438	}
439
440	if ((ipe == NULL) && (hmsk != 0)) {
441		while (hmsk != 0) {
442			msk <<= 1;
443			if (hmsk & 0x80000000)
444				break;
445			hmsk <<= 1;
446		}
447		if (hmsk != 0) {
448			hmsk <<= 1;
449			goto maskloop;
450		}
451	}
452	return ipe;
453}
454
455#endif /* IPFILTER_LOOKUP */
456