4 * Copyright (C) 1993-2001, 2003 by Darren Reed.
6 * See the IPFILTER.LICENCE file for details on licencing.
8 * Copyright 2008 Sun Microsystems, Inc.
10 #if defined(KERNEL) || defined(_KERNEL)
16 #include <sys/param.h>
17 #if defined(__NetBSD__)
18 # if (NetBSD >= 199905) && !defined(IPFILTER_LKM) && defined(_KERNEL)
19 # include "opt_ipfilter.h"
22 #include <sys/types.h>
23 #include <sys/errno.h>
36 #include <sys/socket.h>
37 #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
38 # include <sys/malloc.h>
40 #if defined(__FreeBSD__)
41 # include <sys/cdefs.h>
42 # include <sys/proc.h>
44 #if !defined(__svr4__) && !defined(__SVR4) && !defined(__hpux) && \
46 # include <sys/mbuf.h>
49 # include <sys/systm.h>
53 #include <netinet/in.h>
56 #include "netinet/ip_compat.h"
57 #include "netinet/ip_fil.h"
58 #include "netinet/ip_lookup.h"
59 #include "netinet/ip_htable.h"
63 #if defined(__NetBSD__)
64 #include <sys/cdefs.h>
65 __KERNEL_RCSID(0, "$NetBSD$");
67 static const char rcsid
[] = "@(#)Id: ip_htable.c,v 2.34.2.13 2009/05/13 19:13:36 darrenr Exp";
71 #ifdef IPFILTER_LOOKUP
72 static iphtent_t
*fr_iphmfind
__P((iphtable_t
*, struct in_addr
*));
73 static u_long ipht_nomem
[IPL_LOGSIZE
] = { 0, 0, 0, 0, 0, 0, 0, 0 };
74 static u_long ipf_nhtables
[IPL_LOGSIZE
] = { 0, 0, 0, 0, 0, 0, 0, 0 };
75 static u_long ipf_nhtnodes
[IPL_LOGSIZE
] = { 0, 0, 0, 0, 0, 0, 0, 0 };
77 iphtable_t
*ipf_htables
[IPL_LOGSIZE
] = { NULL
, NULL
, NULL
, NULL
,
78 NULL
, NULL
, NULL
, NULL
};
81 void fr_htable_unload()
85 fop
.iplf_unit
= IPL_LOGALL
;
86 (void)fr_flushhtable(&fop
);
90 int fr_gethtablestat(op
)
95 if (op
->iplo_size
!= sizeof(stats
))
98 stats
.iphs_tables
= ipf_htables
[op
->iplo_unit
];
99 stats
.iphs_numtables
= ipf_nhtables
[op
->iplo_unit
];
100 stats
.iphs_numnodes
= ipf_nhtnodes
[op
->iplo_unit
];
101 stats
.iphs_nomem
= ipht_nomem
[op
->iplo_unit
];
103 return COPYOUT(&stats
, op
->iplo_struct
, sizeof(stats
));
109 * Create a new hash table using the template passed.
114 iphtable_t
*iph
, *oiph
;
115 char name
[FR_GROUPLEN
];
118 unit
= op
->iplo_unit
;
119 if ((op
->iplo_arg
& IPHASH_ANON
) == 0) {
120 iph
= fr_existshtable(unit
, op
->iplo_name
);
122 if ((iph
->iph_flags
& IPHASH_DELETE
) == 0)
124 iph
->iph_flags
&= ~IPHASH_DELETE
;
129 KMALLOC(iph
, iphtable_t
*);
131 ipht_nomem
[op
->iplo_unit
]++;
134 err
= COPYIN(op
->iplo_struct
, iph
, sizeof(*iph
));
140 if (iph
->iph_unit
!= unit
) {
145 if ((op
->iplo_arg
& IPHASH_ANON
) != 0) {
149 #if defined(SNPRINTF) && defined(_KERNEL)
150 SNPRINTF(name
, sizeof(name
), "%u", i
);
152 (void)sprintf(name
, "%u", i
);
154 for (oiph
= ipf_htables
[unit
]; oiph
!= NULL
;
155 oiph
= oiph
->iph_next
)
156 if (strncmp(oiph
->iph_name
, name
,
157 sizeof(oiph
->iph_name
)) == 0)
159 } while (oiph
!= NULL
);
161 (void)strncpy(iph
->iph_name
, name
, sizeof(iph
->iph_name
));
162 (void)strncpy(op
->iplo_name
, name
, sizeof(op
->iplo_name
));
163 iph
->iph_type
|= IPHASH_ANON
;
166 KMALLOCS(iph
->iph_table
, iphtent_t
**,
167 iph
->iph_size
* sizeof(*iph
->iph_table
));
168 if (iph
->iph_table
== NULL
) {
174 bzero((char *)iph
->iph_table
, iph
->iph_size
* sizeof(*iph
->iph_table
));
176 iph
->iph_list
= NULL
;
179 iph
->iph_next
= ipf_htables
[unit
];
180 iph
->iph_pnext
= &ipf_htables
[unit
];
181 if (ipf_htables
[unit
] != NULL
)
182 ipf_htables
[unit
]->iph_pnext
= &iph
->iph_next
;
183 ipf_htables
[unit
] = iph
;
184 ipf_nhtables
[unit
]++;
192 int fr_removehtable(unit
, name
)
198 iph
= fr_findhtable(unit
, name
);
202 if (iph
->iph_unit
!= unit
) {
206 if (iph
->iph_ref
!= 0) {
207 (void) fr_clearhtable(iph
);
208 iph
->iph_flags
|= IPHASH_DELETE
;
218 int fr_clearhtable(iph
)
223 while ((ipe
= iph
->iph_list
) != NULL
)
224 if (fr_delhtent(iph
, ipe
) != 0)
230 int fr_delhtable(iph
)
234 if (fr_clearhtable(iph
) != 0)
237 if (iph
->iph_pnext
!= NULL
)
238 *iph
->iph_pnext
= iph
->iph_next
;
239 if (iph
->iph_next
!= NULL
)
240 iph
->iph_next
->iph_pnext
= iph
->iph_pnext
;
242 ipf_nhtables
[iph
->iph_unit
]--;
244 return fr_derefhtable(iph
);
249 * Delete an entry from a hash table.
251 int fr_delhtent(iph
, ipe
)
256 if (ipe
->ipe_phnext
!= NULL
)
257 *ipe
->ipe_phnext
= ipe
->ipe_hnext
;
258 if (ipe
->ipe_hnext
!= NULL
)
259 ipe
->ipe_hnext
->ipe_phnext
= ipe
->ipe_phnext
;
261 if (ipe
->ipe_pnext
!= NULL
)
262 *ipe
->ipe_pnext
= ipe
->ipe_next
;
263 if (ipe
->ipe_next
!= NULL
)
264 ipe
->ipe_next
->ipe_pnext
= ipe
->ipe_pnext
;
266 switch (iph
->iph_type
& ~IPHASH_ANON
)
268 case IPHASH_GROUPMAP
:
269 if (ipe
->ipe_group
!= NULL
)
270 fr_delgroup(ipe
->ipe_group
, IPL_LOGIPF
, fr_active
);
279 return fr_derefhtent(ipe
);
283 int fr_derefhtable(iph
)
291 if (iph
->iph_ref
== 0) {
292 KFREES(iph
->iph_table
, iph
->iph_size
* sizeof(*iph
->iph_table
));
300 int fr_derefhtent(ipe
)
305 if (ipe
->ipe_ref
== 0) {
306 ipf_nhtnodes
[ipe
->ipe_unit
]--;
317 iphtable_t
*fr_existshtable(unit
, name
)
323 for (iph
= ipf_htables
[unit
]; iph
!= NULL
; iph
= iph
->iph_next
)
324 if (strncmp(iph
->iph_name
, name
, sizeof(iph
->iph_name
)) == 0)
330 iphtable_t
*fr_findhtable(unit
, name
)
336 iph
= fr_existshtable(unit
, name
);
337 if ((iph
!= NULL
) && (iph
->iph_flags
& IPHASH_DELETE
) == 0)
344 size_t fr_flushhtable(op
)
353 for (i
= 0; i
<= IPL_LOGMAX
; i
++) {
354 if (op
->iplf_unit
== i
|| op
->iplf_unit
== IPL_LOGALL
) {
355 while ((iph
= ipf_htables
[i
]) != NULL
) {
356 if (fr_delhtable(iph
) == 0) {
359 iph
->iph_flags
|= IPHASH_DELETE
;
370 * Add an entry to a hash table.
372 int fr_addhtent(iph
, ipeo
)
380 KMALLOC(ipe
, iphtent_t
*);
384 bcopy((char *)ipeo
, (char *)ipe
, sizeof(*ipe
));
385 ipe
->ipe_addr
.in4_addr
&= ipe
->ipe_mask
.in4_addr
;
386 ipe
->ipe_addr
.in4_addr
= ntohl(ipe
->ipe_addr
.in4_addr
);
387 bits
= count4bits(ipe
->ipe_mask
.in4_addr
);
388 ipe
->ipe_mask
.in4_addr
= ntohl(ipe
->ipe_mask
.in4_addr
);
390 hv
= IPE_HASH_FN(ipe
->ipe_addr
.in4_addr
, ipe
->ipe_mask
.in4_addr
,
393 ipe
->ipe_hnext
= iph
->iph_table
[hv
];
394 ipe
->ipe_phnext
= iph
->iph_table
+ hv
;
396 if (iph
->iph_table
[hv
] != NULL
)
397 iph
->iph_table
[hv
]->ipe_phnext
= &ipe
->ipe_hnext
;
398 iph
->iph_table
[hv
] = ipe
;
400 ipe
->ipe_next
= iph
->iph_list
;
401 ipe
->ipe_pnext
= &iph
->iph_list
;
402 if (ipe
->ipe_next
!= NULL
)
403 ipe
->ipe_next
->ipe_pnext
= &ipe
->ipe_next
;
406 if ((bits
>= 0) && (bits
!= 32))
407 iph
->iph_masks
|= 1 << bits
;
409 switch (iph
->iph_type
& ~IPHASH_ANON
)
411 case IPHASH_GROUPMAP
:
412 ipe
->ipe_ptr
= fr_addgroup(ipe
->ipe_group
, NULL
,
413 iph
->iph_flags
, IPL_LOGIPF
,
423 ipe
->ipe_unit
= iph
->iph_unit
;
424 ipf_nhtnodes
[ipe
->ipe_unit
]++;
430 void *fr_iphmfindgroup(tptr
, aptr
)
433 struct in_addr
*addr
;
438 READ_ENTER(&ip_poolrw
);
442 ipe
= fr_iphmfind(iph
, addr
);
447 RWLOCK_EXIT(&ip_poolrw
);
452 /* ------------------------------------------------------------------------ */
453 /* Function: fr_iphmfindip */
454 /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */
455 /* Parameters: tptr(I) - pointer to the pool to search */
456 /* ipversion(I) - IP protocol version (4 or 6) */
457 /* aptr(I) - pointer to address information */
459 /* Search the hash table for a given address and return a search result. */
460 /* ------------------------------------------------------------------------ */
461 int fr_iphmfindip(tptr
, ipversion
, aptr
)
465 struct in_addr
*addr
;
473 if (tptr
== NULL
|| aptr
== NULL
)
479 READ_ENTER(&ip_poolrw
);
480 ipe
= fr_iphmfind(iph
, addr
);
485 RWLOCK_EXIT(&ip_poolrw
);
490 /* Locks: ip_poolrw */
491 static iphtent_t
*fr_iphmfind(iph
, addr
)
493 struct in_addr
*addr
;
495 u_32_t hmsk
, msk
, ips
;
499 hmsk
= iph
->iph_masks
;
502 ips
= ntohl(addr
->s_addr
) & msk
;
503 hv
= IPE_HASH_FN(ips
, msk
, iph
->iph_size
);
504 for (ipe
= iph
->iph_table
[hv
]; (ipe
!= NULL
); ipe
= ipe
->ipe_hnext
) {
505 if (ipe
->ipe_mask
.in4_addr
!= msk
||
506 ipe
->ipe_addr
.in4_addr
!= ips
) {
512 if ((ipe
== NULL
) && (hmsk
!= 0)) {
515 if (hmsk
& 0x80000000)
528 int fr_htable_getnext(token
, ilp
)
530 ipflookupiter_t
*ilp
;
532 iphtent_t
*node
, zn
, *nextnode
;
533 iphtable_t
*iph
, zp
, *nextiph
;
542 READ_ENTER(&ip_poolrw
);
545 * Get "previous" entry from the token and find the next entry.
547 * If we found an entry, add a reference to it and update the token.
548 * Otherwise, zero out data to be returned and NULL out token.
550 switch (ilp
->ili_otype
)
552 case IPFLOOKUPITER_LIST
:
553 iph
= token
->ipt_data
;
555 nextiph
= ipf_htables
[(int)ilp
->ili_unit
];
557 nextiph
= iph
->iph_next
;
560 if (nextiph
!= NULL
) {
561 ATOMIC_INC(nextiph
->iph_ref
);
562 token
->ipt_data
= nextiph
;
564 bzero((char *)&zp
, sizeof(zp
));
566 token
->ipt_data
= NULL
;
570 case IPFLOOKUPITER_NODE
:
571 node
= token
->ipt_data
;
573 iph
= fr_findhtable(ilp
->ili_unit
, ilp
->ili_name
);
577 nextnode
= iph
->iph_list
;
580 nextnode
= node
->ipe_next
;
583 if (nextnode
!= NULL
) {
584 ATOMIC_INC(nextnode
->ipe_ref
);
585 token
->ipt_data
= nextnode
;
587 bzero((char *)&zn
, sizeof(zn
));
589 token
->ipt_data
= NULL
;
599 * Now that we have ref, it's save to give up lock.
601 RWLOCK_EXIT(&ip_poolrw
);
606 * Copy out data and clean up references and token as needed.
608 switch (ilp
->ili_otype
)
610 case IPFLOOKUPITER_LIST
:
611 err
= COPYOUT(nextiph
, ilp
->ili_data
, sizeof(*nextiph
));
614 if (token
->ipt_data
!= NULL
) {
616 WRITE_ENTER(&ip_poolrw
);
618 RWLOCK_EXIT(&ip_poolrw
);
620 if (nextiph
->iph_next
== NULL
)
621 token
->ipt_data
= NULL
;
625 case IPFLOOKUPITER_NODE
:
626 err
= COPYOUT(nextnode
, ilp
->ili_data
, sizeof(*nextnode
));
629 if (token
->ipt_data
!= NULL
) {
631 WRITE_ENTER(&ip_poolrw
);
633 RWLOCK_EXIT(&ip_poolrw
);
635 if (nextnode
->ipe_next
== NULL
)
636 token
->ipt_data
= NULL
;
645 void fr_htable_iterderef(otype
, unit
, data
)
654 if (unit
< 0 || unit
> IPL_LOGMAX
)
659 case IPFLOOKUPITER_LIST
:
660 WRITE_ENTER(&ip_poolrw
);
661 fr_derefhtable((iphtable_t
*)data
);
662 RWLOCK_EXIT(&ip_poolrw
);
665 case IPFLOOKUPITER_NODE
:
666 WRITE_ENTER(&ip_poolrw
);
667 fr_derefhtent((iphtent_t
*)data
);
668 RWLOCK_EXIT(&ip_poolrw
);
675 #endif /* IPFILTER_LOOKUP */