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