2 * Copyright (C) 1993-2001, 2003 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
6 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
9 #if defined(KERNEL) || defined(_KERNEL)
15 #include <sys/param.h>
16 #include <sys/types.h>
17 #include <sys/errno.h>
30 #include <sys/socket.h>
31 #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000)
32 # include <sys/malloc.h>
34 #if defined(__FreeBSD__)
35 # include <sys/cdefs.h>
36 # include <sys/proc.h>
38 #if !defined(__svr4__) && !defined(__SVR4) && !defined(__hpux) && \
40 # include <sys/mbuf.h>
43 # include <sys/systm.h>
47 #include <netinet/in.h>
50 #include "netinet/ip_compat.h"
51 #include "netinet/ip_fil.h"
52 #include "netinet/ip_lookup.h"
53 #include "netinet/ip_htable.h"
54 #include "netinet/ipf_stack.h"
58 static const char rcsid
[] = "@(#)$Id: ip_htable.c,v 2.34.2.3 2005/05/14 05:11:38 darrenr Exp $";
61 #ifdef IPFILTER_LOOKUP
62 static iphtent_t
*fr_iphmfind
__P((iphtable_t
*, struct in_addr
*));
64 static iphtent_t
*fr_iphmfind6
__P((iphtable_t
*, struct in6_addr
*));
65 static uint32_t sum4(uint32_t *);
66 static void left_shift_ipv6
__P((char *));
69 void fr_htable_unload(ifs
)
74 fop
.iplf_unit
= IPL_LOGALL
;
75 (void)fr_flushhtable(&fop
, ifs
);
79 int fr_gethtablestat(op
, ifs
)
85 if (op
->iplo_size
!= sizeof(stats
))
88 stats
.iphs_tables
= ifs
->ifs_ipf_htables
[op
->iplo_unit
];
89 stats
.iphs_numtables
= ifs
->ifs_ipf_nhtables
[op
->iplo_unit
];
90 stats
.iphs_numnodes
= ifs
->ifs_ipf_nhtnodes
[op
->iplo_unit
];
91 stats
.iphs_nomem
= ifs
->ifs_ipht_nomem
[op
->iplo_unit
];
93 return COPYOUT(&stats
, op
->iplo_struct
, sizeof(stats
));
99 * Create a new hash table using the template passed.
101 int fr_newhtable(op
, ifs
)
105 iphtable_t
*iph
, *oiph
;
106 char name
[FR_GROUPLEN
];
109 KMALLOC(iph
, iphtable_t
*);
111 ifs
->ifs_ipht_nomem
[op
->iplo_unit
]++;
115 err
= COPYIN(op
->iplo_struct
, iph
, sizeof(*iph
));
121 unit
= op
->iplo_unit
;
122 if (iph
->iph_unit
!= unit
) {
127 if ((op
->iplo_arg
& IPHASH_ANON
) == 0) {
128 if (fr_findhtable(op
->iplo_unit
, op
->iplo_name
, ifs
) != NULL
) {
136 #if defined(SNPRINTF) && defined(_KERNEL)
137 (void)SNPRINTF(name
, sizeof(name
), "%u", i
);
139 (void)sprintf(name
, "%u", i
);
141 for (oiph
= ifs
->ifs_ipf_htables
[unit
]; oiph
!= NULL
;
142 oiph
= oiph
->iph_next
)
143 if (strncmp(oiph
->iph_name
, name
,
144 sizeof(oiph
->iph_name
)) == 0)
146 } while (oiph
!= NULL
);
147 (void)strncpy(iph
->iph_name
, name
, sizeof(iph
->iph_name
));
148 err
= COPYOUT(iph
, op
->iplo_struct
, sizeof(*iph
));
153 iph
->iph_type
|= IPHASH_ANON
;
156 KMALLOCS(iph
->iph_table
, iphtent_t
**,
157 iph
->iph_size
* sizeof(*iph
->iph_table
));
158 if (iph
->iph_table
== NULL
) {
160 ifs
->ifs_ipht_nomem
[unit
]++;
164 bzero((char *)iph
->iph_table
, iph
->iph_size
* sizeof(*iph
->iph_table
));
165 iph
->iph_masks
[0] = 0;
166 iph
->iph_masks
[1] = 0;
167 iph
->iph_masks
[2] = 0;
168 iph
->iph_masks
[3] = 0;
169 iph
->iph_list
= NULL
;
172 iph
->iph_next
= ifs
->ifs_ipf_htables
[unit
];
173 iph
->iph_pnext
= &ifs
->ifs_ipf_htables
[unit
];
174 if (ifs
->ifs_ipf_htables
[unit
] != NULL
)
175 ifs
->ifs_ipf_htables
[unit
]->iph_pnext
= &iph
->iph_next
;
176 ifs
->ifs_ipf_htables
[unit
] = iph
;
178 ifs
->ifs_ipf_nhtables
[unit
]++;
186 int fr_removehtable(op
, ifs
)
193 iph
= fr_findhtable(op
->iplo_unit
, op
->iplo_name
, ifs
);
197 if (iph
->iph_unit
!= op
->iplo_unit
) {
201 if (iph
->iph_ref
!= 1) {
205 fr_delhtable(iph
, ifs
);
211 void fr_delhtable(iph
, ifs
)
218 for (i
= 0; i
< iph
->iph_size
; i
++)
219 while ((ipe
= iph
->iph_table
[i
]) != NULL
)
220 if (fr_delhtent(iph
, ipe
, ifs
) != 0)
223 *iph
->iph_pnext
= iph
->iph_next
;
224 if (iph
->iph_next
!= NULL
)
225 iph
->iph_next
->iph_pnext
= iph
->iph_pnext
;
227 ifs
->ifs_ipf_nhtables
[iph
->iph_unit
]--;
229 if (iph
->iph_ref
== 1) {
230 KFREES(iph
->iph_table
, iph
->iph_size
* sizeof(*iph
->iph_table
));
236 void fr_derefhtable(iph
, ifs
)
241 if (iph
->iph_ref
== 0)
242 fr_delhtable(iph
, ifs
);
246 void fr_derefhtent(ipe
)
250 if (ipe
->ipe_ref
== 0) {
256 iphtable_t
*fr_findhtable(unit
, name
, ifs
)
263 for (iph
= ifs
->ifs_ipf_htables
[unit
]; iph
!= NULL
; iph
= iph
->iph_next
)
264 if (strncmp(iph
->iph_name
, name
, sizeof(iph
->iph_name
)) == 0)
270 size_t fr_flushhtable(op
, ifs
)
280 for (i
= 0; i
<= IPL_LOGMAX
; i
++) {
281 if (op
->iplf_unit
== i
|| op
->iplf_unit
== IPL_LOGALL
) {
282 while ((iph
= ifs
->ifs_ipf_htables
[i
]) != NULL
) {
283 fr_delhtable(iph
, ifs
);
294 * Add an entry to a hash table.
296 int fr_addhtent(iph
, ipeo
, ifs
)
305 KMALLOC(ipe
, iphtent_t
*);
309 bcopy((char *)ipeo
, (char *)ipe
, sizeof(*ipe
));
311 if (ipe
->ipe_family
== AF_INET6
) {
312 bits
= count6bits((u_32_t
*)ipe
->ipe_mask
.in6_addr8
);
313 hv
= IPE_HASH_FN(sum4((uint32_t *)ipe
->ipe_addr
.in6_addr8
),
314 sum4((uint32_t *)ipe
->ipe_mask
.in6_addr8
),
318 if (ipe
->ipe_family
== AF_INET
)
320 ipe
->ipe_addr
.in4_addr
&= ipe
->ipe_mask
.in4_addr
;
321 ipe
->ipe_addr
.in4_addr
= ntohl(ipe
->ipe_addr
.in4_addr
);
322 bits
= count4bits(ipe
->ipe_mask
.in4_addr
);
323 ipe
->ipe_mask
.in4_addr
= ntohl(ipe
->ipe_mask
.in4_addr
);
325 hv
= IPE_HASH_FN(ipe
->ipe_addr
.in4_addr
, ipe
->ipe_mask
.in4_addr
,
331 ipe
->ipe_next
= iph
->iph_table
[hv
];
332 ipe
->ipe_pnext
= iph
->iph_table
+ hv
;
334 if (iph
->iph_table
[hv
] != NULL
)
335 iph
->iph_table
[hv
]->ipe_pnext
= &ipe
->ipe_next
;
336 iph
->iph_table
[hv
] = ipe
;
338 ipe
->ipe_snext
= iph
->iph_list
;
339 ipe
->ipe_psnext
= &iph
->iph_list
;
340 if (ipe
->ipe_next
!= NULL
)
341 ipe
->ipe_next
->ipe_psnext
= &ipe
->ipe_snext
;
345 if (ipe
->ipe_family
== AF_INET6
) {
346 if ((bits
>= 0) && (bits
!= 128))
348 iph
->iph_masks
[0] |= 1 << (bits
- 96);
350 iph
->iph_masks
[1] |= 1 << (bits
- 64);
352 iph
->iph_masks
[2] |= 1 << (bits
- 32);
354 iph
->iph_masks
[3] |= 1 << bits
;
359 if ((bits
>= 0) && (bits
!= 32))
360 iph
->iph_masks
[3] |= 1 << bits
;
363 switch (iph
->iph_type
& ~IPHASH_ANON
)
365 case IPHASH_GROUPMAP
:
366 ipe
->ipe_ptr
= fr_addgroup(ipe
->ipe_group
, NULL
,
367 iph
->iph_flags
, IPL_LOGIPF
,
368 ifs
->ifs_fr_active
, ifs
);
377 ifs
->ifs_ipf_nhtnodes
[iph
->iph_unit
]++;
384 * Delete an entry from a hash table.
386 int fr_delhtent(iph
, ipe
, ifs
)
391 if (ipe
->ipe_ref
!= 1)
395 *ipe
->ipe_pnext
= ipe
->ipe_next
;
396 if (ipe
->ipe_next
!= NULL
)
397 ipe
->ipe_next
->ipe_pnext
= ipe
->ipe_pnext
;
399 switch (iph
->iph_type
& ~IPHASH_ANON
)
401 case IPHASH_GROUPMAP
:
402 if (ipe
->ipe_group
!= NULL
)
403 fr_delgroup(ipe
->ipe_group
, IPL_LOGIPF
,
404 ifs
->ifs_fr_active
, ifs
);
415 ifs
->ifs_ipf_nhtnodes
[iph
->iph_unit
]--;
421 void *fr_iphmfindgroup(tptr
, version
, aptr
, ifs
)
439 READ_ENTER(&ifs
->ifs_ip_poolrw
);
445 ipe
= fr_iphmfind6(iph
, &addr
->in6
);
449 ipe
= fr_iphmfind(iph
, &addr
->in4
);
456 RWLOCK_EXIT(&ifs
->ifs_ip_poolrw
);
461 /* ------------------------------------------------------------------------ */
462 /* Function: fr_iphmfindip */
463 /* Returns: int - 0 == +ve match, -1 == error, 1 == -ve/no match */
464 /* Parameters: tptr(I) - pointer to the pool to search */
465 /* version(I) - IP protocol version (4 or 6) */
466 /* aptr(I) - pointer to address information */
467 /* fin - pointer to packet information */
468 /* ifs - ipf stack instance */
470 /* Search the hash table for a given address and return a search result. */
471 /* ------------------------------------------------------------------------ */
472 int fr_iphmfindip(tptr
, version
, aptr
, fin
, ifs
)
490 if (tptr
== NULL
|| aptr
== NULL
)
496 READ_ENTER(&ifs
->ifs_ip_poolrw
);
499 ipe
= fr_iphmfind6(iph
, &addr
->in6
);
503 ipe
= fr_iphmfind(iph
, &addr
->in4
);
508 ipe
->ipe_bytes
+= fin
->fin_plen
;
513 RWLOCK_EXIT(&ifs
->ifs_ip_poolrw
);
518 /* Locks: ip_poolrw */
519 static iphtent_t
*fr_iphmfind(iph
, addr
)
521 struct in_addr
*addr
;
523 u_32_t hmsk
, msk
, ips
;
527 hmsk
= iph
->iph_masks
[3];
530 ips
= ntohl(addr
->s_addr
) & msk
;
531 hv
= IPE_HASH_FN(ips
, msk
, iph
->iph_size
);
532 for (ipe
= iph
->iph_table
[hv
]; (ipe
!= NULL
); ipe
= ipe
->ipe_next
) {
533 if (ipe
->ipe_mask
.in4_addr
!= msk
||
534 ipe
->ipe_addr
.in4_addr
!= ips
) {
540 if ((ipe
== NULL
) && (hmsk
!= 0)) {
543 if (hmsk
& 0x80000000)
557 /* Locks: ip_poolrw */
558 static iphtent_t
*fr_iphmfind6(iph
, addr
)
560 struct in6_addr
*addr
;
562 u_32_t hmsk
[4], msk
[4], ips
[4], *and;
566 hmsk
[0] = iph
->iph_masks
[0];
567 hmsk
[1] = iph
->iph_masks
[1];
568 hmsk
[2] = iph
->iph_masks
[2];
569 hmsk
[3] = iph
->iph_masks
[3];
576 and = (u_32_t
*)addr
->s6_addr
;
577 ips
[0] = *and & msk
[0];
578 ips
[1] = *(and + 1) & msk
[1];
579 ips
[2] = *(and + 2) & msk
[2];
580 ips
[3] = *(and + 3) & msk
[3];
582 hv
= IPE_HASH_FN(sum4((uint32_t *)addr
), sum4((uint32_t *)msk
),
584 for (ipe
= iph
->iph_table
[hv
]; (ipe
!= NULL
); ipe
= ipe
->ipe_next
) {
585 if (bcmp((void *)&ipe
->ipe_mask
.in6
, (void *)msk
, 16) ||
586 bcmp((void *)&ipe
->ipe_addr
.in6
, (void *)ips
, 16))
591 if ((ipe
== NULL
) && ((hmsk
[0] != 0) ||
595 while ((hmsk
[0] != 0) && (hmsk
[1] != 0) &&
596 (hmsk
[2] != 0) && (hmsk
[3] != 0)) {
597 left_shift_ipv6((char *)msk
);
598 if (hmsk
[0] & 0x80000000)
600 left_shift_ipv6((char *)hmsk
);
602 if ((hmsk
[0] != 0) && (hmsk
[1] != 0) &&
603 (hmsk
[2] != 0) && (hmsk
[3] != 0)) {
604 left_shift_ipv6((char *)hmsk
);
613 * sum4: ipv6 add -> 4 bytes values
615 static uint32_t sum4(add
)
618 return (*add
+ *(add
+ 1) + *(add
+ 2) + *(add
+ 3));
622 * left shift on 128 bits
624 static void left_shift_ipv6(data
)
631 if (sd
[1] >= 0x80000000)
635 if (sd
[2] >= 0x80000000)
639 if (sd
[3] >= 0x80000000)
646 int fr_htable_getnext(token
, ilp
, ifs
)
648 ipflookupiter_t
*ilp
;
651 iphtent_t
*node
, zn
, *nextnode
;
652 iphtable_t
*iph
, zp
, *nextiph
;
661 READ_ENTER(&ifs
->ifs_ip_poolrw
);
664 * Get "previous" entry from the token and find the next entry.
666 * If we found an entry, add a reference to it and update the token.
667 * Otherwise, zero out data to be returned and NULL out token.
669 switch (ilp
->ili_otype
)
671 case IPFLOOKUPITER_LIST
:
672 iph
= token
->ipt_data
;
674 nextiph
= ifs
->ifs_ipf_htables
[(int)ilp
->ili_unit
];
676 nextiph
= iph
->iph_next
;
678 if (nextiph
!= NULL
) {
679 ATOMIC_INC(nextiph
->iph_ref
);
680 token
->ipt_data
= nextiph
;
682 bzero((char *)&zp
, sizeof(zp
));
684 token
->ipt_data
= NULL
;
688 case IPFLOOKUPITER_NODE
:
689 node
= token
->ipt_data
;
691 iph
= fr_findhtable(ilp
->ili_unit
, ilp
->ili_name
, ifs
);
695 nextnode
= iph
->iph_list
;
698 nextnode
= node
->ipe_snext
;
700 if (nextnode
!= NULL
) {
701 ATOMIC_INC(nextnode
->ipe_ref
);
702 token
->ipt_data
= nextnode
;
704 bzero((char *)&zn
, sizeof(zn
));
706 token
->ipt_data
= NULL
;
716 * Now that we have ref, it's save to give up lock.
718 RWLOCK_EXIT(&ifs
->ifs_ip_poolrw
);
723 * Copy out data and clean up references and token as needed.
725 switch (ilp
->ili_otype
)
727 case IPFLOOKUPITER_LIST
:
728 err
= COPYOUT(nextiph
, ilp
->ili_data
, sizeof(*nextiph
));
731 if (token
->ipt_data
== NULL
) {
732 ipf_freetoken(token
, ifs
);
735 WRITE_ENTER(&ifs
->ifs_ip_poolrw
);
736 fr_derefhtable(iph
, ifs
);
737 RWLOCK_EXIT(&ifs
->ifs_ip_poolrw
);
739 if (nextiph
->iph_next
== NULL
)
740 ipf_freetoken(token
, ifs
);
744 case IPFLOOKUPITER_NODE
:
745 err
= COPYOUT(nextnode
, ilp
->ili_data
, sizeof(*nextnode
));
748 if (token
->ipt_data
== NULL
) {
749 ipf_freetoken(token
, ifs
);
752 WRITE_ENTER(&ifs
->ifs_ip_poolrw
);
754 RWLOCK_EXIT(&ifs
->ifs_ip_poolrw
);
756 if (nextnode
->ipe_snext
== NULL
)
757 ipf_freetoken(token
, ifs
);
766 void fr_htable_iterderef(otype
, unit
, data
, ifs
)
776 if (unit
< 0 || unit
> IPL_LOGMAX
)
781 case IPFLOOKUPITER_LIST
:
782 WRITE_ENTER(&ifs
->ifs_ip_poolrw
);
783 fr_derefhtable((iphtable_t
*)data
, ifs
);
784 RWLOCK_EXIT(&ifs
->ifs_ip_poolrw
);
787 case IPFLOOKUPITER_NODE
:
788 WRITE_ENTER(&ifs
->ifs_ip_poolrw
);
789 fr_derefhtent((iphtent_t
*)data
);
790 RWLOCK_EXIT(&ifs
->ifs_ip_poolrw
);
796 #endif /* IPFILTER_LOOKUP */