1/*
2 * Copyright (C) 2002-2003 by Darren Reed.
3 *
4 * See the IPFILTER.LICENCE file for details on licencing.
5 *
6 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
7 * Use is subject to license terms.
8 */
9
10#if defined(KERNEL) || defined(_KERNEL)
11# undef KERNEL
12# undef _KERNEL
13# define        KERNEL	1
14# define        _KERNEL	1
15#endif
16#if defined(__osf__)
17# define _PROTO_NET_H_
18#endif
19#include <sys/param.h>
20#include <sys/errno.h>
21#include <sys/types.h>
22#include <sys/time.h>
23#include <sys/file.h>
24#if __FreeBSD_version >= 220000 && defined(_KERNEL)
25# include <sys/fcntl.h>
26# include <sys/filio.h>
27#else
28# include <sys/ioctl.h>
29#endif
30#if !defined(_KERNEL)
31# include <string.h>
32# define _KERNEL
33# ifdef __OpenBSD__
34struct file;
35# endif
36# include <sys/uio.h>
37# undef _KERNEL
38#endif
39#include <sys/socket.h>
40#if (defined(__osf__) || defined(AIX) || defined(__hpux) || defined(__sgi)) && defined(_KERNEL)
41# ifdef __osf__
42#  include <net/radix.h>
43# endif
44# include "radix_ipf_local.h"
45# define _RADIX_H_
46#endif
47#include <net/if.h>
48#if defined(__FreeBSD__)
49#  include <sys/cdefs.h>
50#  include <sys/proc.h>
51#endif
52#if defined(_KERNEL)
53# include <sys/systm.h>
54# if !defined(__SVR4) && !defined(__svr4__)
55#  include <sys/mbuf.h>
56# endif
57#endif
58#include <netinet/in.h>
59
60#include "netinet/ip_compat.h"
61#include "netinet/ip_fil.h"
62#include "netinet/ip_pool.h"
63#include "netinet/ip_htable.h"
64#include "netinet/ip_lookup.h"
65#include "netinet/ipf_stack.h"
66/* END OF INCLUDES */
67
68#if !defined(lint)
69static const char rcsid[] = "@(#)$Id: ip_lookup.c,v 2.35.2.7 2005/06/12 07:18:20 darrenr Exp $";
70#endif
71
72#ifdef	IPFILTER_LOOKUP
73static int iplookup_addnode __P((caddr_t, ipf_stack_t *));
74static int iplookup_delnode __P((caddr_t data, ipf_stack_t *));
75static int iplookup_addtable __P((caddr_t, ipf_stack_t *));
76static int iplookup_deltable __P((caddr_t, ipf_stack_t *));
77static int iplookup_stats __P((caddr_t, ipf_stack_t *));
78static int iplookup_flush __P((caddr_t, ipf_stack_t *));
79
80
81
82/* ------------------------------------------------------------------------ */
83/* Function:    iplookup_init                                               */
84/* Returns:     int     - 0 = success, else error                           */
85/* Parameters:  Nil                                                         */
86/*                                                                          */
87/* Initialise all of the subcomponents of the lookup infrstructure.         */
88/* ------------------------------------------------------------------------ */
89int ip_lookup_init(ifs)
90ipf_stack_t *ifs;
91{
92
93	if (ip_pool_init(ifs) == -1)
94		return -1;
95
96	RWLOCK_INIT(&ifs->ifs_ip_poolrw, "ip pool rwlock");
97
98	ifs->ifs_ip_lookup_inited = 1;
99	ifs->ifs_ipftokenhead = NULL;
100	ifs->ifs_ipftokentail = &ifs->ifs_ipftokenhead;
101	return 0;
102}
103
104
105/* ------------------------------------------------------------------------ */
106/* Function:    iplookup_unload                                             */
107/* Returns:     int     - 0 = success, else error                           */
108/* Parameters:  Nil                                                         */
109/*                                                                          */
110/* Free up all pool related memory that has been allocated whilst IPFilter  */
111/* has been running.  Also, do any other deinitialisation required such     */
112/* ip_lookup_init() can be called again, safely.                            */
113/* ------------------------------------------------------------------------ */
114void ip_lookup_unload(ifs)
115ipf_stack_t *ifs;
116{
117	ip_pool_fini(ifs);
118	fr_htable_unload(ifs);
119
120	if (ifs->ifs_ip_lookup_inited == 1) {
121		RW_DESTROY(&ifs->ifs_ip_poolrw);
122		ifs->ifs_ip_lookup_inited = 0;
123	}
124}
125
126
127/* ------------------------------------------------------------------------ */
128/* Function:    iplookup_ioctl                                              */
129/* Returns:     int      - 0 = success, else error                          */
130/* Parameters:  data(IO) - pointer to ioctl data to be copied to/from user  */
131/*                         space.                                           */
132/*              cmd(I)   - ioctl command number                             */
133/*              mode(I)  - file mode bits used with open                    */
134/*                                                                          */
135/* Handle ioctl commands sent to the ioctl device.  For the most part, this */
136/* involves just calling another function to handle the specifics of each   */
137/* command.                                                                 */
138/* ------------------------------------------------------------------------ */
139int ip_lookup_ioctl(data, cmd, mode, uid, ctx, ifs)
140caddr_t data;
141ioctlcmd_t cmd;
142int mode, uid;
143void *ctx;
144ipf_stack_t *ifs;
145{
146	int err;
147	SPL_INT(s);
148
149	mode = mode;	/* LINT */
150
151	SPL_NET(s);
152
153	switch (cmd)
154	{
155	case SIOCLOOKUPADDNODE :
156	case SIOCLOOKUPADDNODEW :
157		WRITE_ENTER(&ifs->ifs_ip_poolrw);
158		err = iplookup_addnode(data, ifs);
159		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
160		break;
161
162	case SIOCLOOKUPDELNODE :
163	case SIOCLOOKUPDELNODEW :
164		WRITE_ENTER(&ifs->ifs_ip_poolrw);
165		err = iplookup_delnode(data, ifs);
166		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
167		break;
168
169	case SIOCLOOKUPADDTABLE :
170		WRITE_ENTER(&ifs->ifs_ip_poolrw);
171		err = iplookup_addtable(data, ifs);
172		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
173		break;
174
175	case SIOCLOOKUPDELTABLE :
176		WRITE_ENTER(&ifs->ifs_ip_poolrw);
177		err = iplookup_deltable(data, ifs);
178		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
179		break;
180
181	case SIOCLOOKUPSTAT :
182	case SIOCLOOKUPSTATW :
183		WRITE_ENTER(&ifs->ifs_ip_poolrw);
184		err = iplookup_stats(data, ifs);
185		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
186		break;
187
188	case SIOCLOOKUPFLUSH :
189		WRITE_ENTER(&ifs->ifs_ip_poolrw);
190		err = iplookup_flush(data, ifs);
191		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
192		break;
193
194	case SIOCLOOKUPITER :
195		err = ip_lookup_iterate(data, uid, ctx, ifs);
196		break;
197
198	default :
199		err = EINVAL;
200		break;
201	}
202	SPL_X(s);
203	return err;
204}
205
206
207/* ------------------------------------------------------------------------ */
208/* Function:    iplookup_addnode                                            */
209/* Returns:     int     - 0 = success, else error                           */
210/* Parameters:  data(I) - pointer to data from ioctl call                   */
211/*                                                                          */
212/* Add a new data node to a lookup structure.  First, check to see if the   */
213/* parent structure refered to by name exists and if it does, then go on to */
214/* add a node to it.                                                        */
215/* ------------------------------------------------------------------------ */
216static int iplookup_addnode(data, ifs)
217caddr_t data;
218ipf_stack_t *ifs;
219{
220	ip_pool_node_t node, *m;
221	iplookupop_t op;
222	iphtable_t *iph;
223	iphtent_t hte;
224	ip_pool_t *p;
225	int err;
226
227	err = BCOPYIN(data, &op, sizeof(op));
228	if (err != 0)
229		return EFAULT;
230
231	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
232
233	switch (op.iplo_type)
234	{
235	case IPLT_POOL :
236		if (op.iplo_size != sizeof(node))
237			return EINVAL;
238
239		err = COPYIN(op.iplo_struct, &node, sizeof(node));
240		if (err != 0)
241			return EFAULT;
242
243		p = ip_pool_find(op.iplo_unit, op.iplo_name, ifs);
244		if (p == NULL)
245			return ESRCH;
246
247		/*
248		 * add an entry to a pool - return an error if it already
249		 * exists remove an entry from a pool - if it exists
250		 * - in both cases, the pool *must* exist!
251		 */
252		m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask);
253		if (m)
254			return EEXIST;
255		err = ip_pool_insert(p, &node.ipn_addr,
256				     &node.ipn_mask, node.ipn_info, ifs);
257		break;
258
259	case IPLT_HASH :
260		if (op.iplo_size != sizeof(hte))
261			return EINVAL;
262
263		err = COPYIN(op.iplo_struct, &hte, sizeof(hte));
264		if (err != 0)
265			return EFAULT;
266
267		iph = fr_findhtable(op.iplo_unit, op.iplo_name, ifs);
268		if (iph == NULL)
269			return ESRCH;
270		err = fr_addhtent(iph, &hte, ifs);
271		break;
272
273	default :
274		err = EINVAL;
275		break;
276	}
277	return err;
278}
279
280
281/* ------------------------------------------------------------------------ */
282/* Function:    iplookup_delnode                                            */
283/* Returns:     int     - 0 = success, else error                           */
284/* Parameters:  data(I) - pointer to data from ioctl call                   */
285/*                                                                          */
286/* Delete a node from a lookup table by first looking for the table it is   */
287/* in and then deleting the entry that gets found.                          */
288/* ------------------------------------------------------------------------ */
289static int iplookup_delnode(data, ifs)
290caddr_t data;
291ipf_stack_t *ifs;
292{
293	ip_pool_node_t node, *m;
294	iplookupop_t op;
295	iphtable_t *iph;
296	iphtent_t hte;
297	ip_pool_t *p;
298	int err;
299
300	err = BCOPYIN(data, &op, sizeof(op));
301	if (err != 0)
302		return EFAULT;
303
304	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
305
306	switch (op.iplo_type)
307	{
308	case IPLT_POOL :
309		if (op.iplo_size != sizeof(node))
310			return EINVAL;
311
312		err = COPYIN(op.iplo_struct, &node, sizeof(node));
313		if (err != 0)
314			return EFAULT;
315
316		p = ip_pool_find(op.iplo_unit, op.iplo_name, ifs);
317		if (!p)
318			return ESRCH;
319
320		m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask);
321		if (m == NULL)
322			return ENOENT;
323		err = ip_pool_remove(p, m, ifs);
324		break;
325
326	case IPLT_HASH :
327		if (op.iplo_size != sizeof(hte))
328			return EINVAL;
329
330		err = COPYIN(op.iplo_struct, &hte, sizeof(hte));
331		if (err != 0)
332			return EFAULT;
333
334		iph = fr_findhtable(op.iplo_unit, op.iplo_name, ifs);
335		if (iph == NULL)
336			return ESRCH;
337		err = fr_delhtent(iph, &hte, ifs);
338		break;
339
340	default :
341		err = EINVAL;
342		break;
343	}
344	return err;
345}
346
347
348/* ------------------------------------------------------------------------ */
349/* Function:    iplookup_addtable                                           */
350/* Returns:     int     - 0 = success, else error                           */
351/* Parameters:  data(I) - pointer to data from ioctl call                   */
352/*                                                                          */
353/* Create a new lookup table, if one doesn't already exist using the name   */
354/* for this one.                                                            */
355/* ------------------------------------------------------------------------ */
356static int iplookup_addtable(data, ifs)
357caddr_t data;
358ipf_stack_t *ifs;
359{
360	iplookupop_t op;
361	int err;
362
363	err = BCOPYIN(data, &op, sizeof(op));
364	if (err != 0)
365		return EFAULT;
366
367	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
368
369	switch (op.iplo_type)
370	{
371	case IPLT_POOL :
372		if (ip_pool_find(op.iplo_unit, op.iplo_name, ifs) != NULL)
373			err = EEXIST;
374		else
375			err = ip_pool_create(&op, ifs);
376		break;
377
378	case IPLT_HASH :
379		if (fr_findhtable(op.iplo_unit, op.iplo_name, ifs) != NULL)
380			err = EEXIST;
381		else
382			err = fr_newhtable(&op, ifs);
383		break;
384
385	default :
386		err = EINVAL;
387		break;
388	}
389	return err;
390}
391
392
393/* ------------------------------------------------------------------------ */
394/* Function:    iplookup_deltable                                           */
395/* Returns:     int     - 0 = success, else error                           */
396/* Parameters:  data(I) - pointer to data from ioctl call                   */
397/*                                                                          */
398/* Decodes ioctl request to remove a particular hash table or pool and      */
399/* calls the relevant function to do the cleanup.                           */
400/* ------------------------------------------------------------------------ */
401static int iplookup_deltable(data, ifs)
402caddr_t data;
403ipf_stack_t *ifs;
404{
405	iplookupop_t op;
406	int err;
407
408	err = BCOPYIN(data, &op, sizeof(op));
409	if (err != 0)
410		return EFAULT;
411
412	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
413
414	if (op.iplo_arg & IPLT_ANON)
415		op.iplo_arg &= IPLT_ANON;
416
417	/*
418	 * create a new pool - fail if one already exists with
419	 * the same #
420	 */
421	switch (op.iplo_type)
422	{
423	case IPLT_POOL :
424		err = ip_pool_destroy(&op, ifs);
425		break;
426
427	case IPLT_HASH :
428		err = fr_removehtable(&op, ifs);
429		break;
430
431	default :
432		err = EINVAL;
433		break;
434	}
435	return err;
436}
437
438
439/* ------------------------------------------------------------------------ */
440/* Function:    iplookup_stats                                              */
441/* Returns:     int     - 0 = success, else error                           */
442/* Parameters:  data(I) - pointer to data from ioctl call                   */
443/*                                                                          */
444/* Copy statistical information from inside the kernel back to user space.  */
445/* ------------------------------------------------------------------------ */
446static int iplookup_stats(data, ifs)
447caddr_t data;
448ipf_stack_t *ifs;
449{
450	iplookupop_t op;
451	int err;
452
453	err = BCOPYIN(data, &op, sizeof(op));
454	if (err != 0)
455		return EFAULT;
456
457	switch (op.iplo_type)
458	{
459	case IPLT_POOL :
460		err = ip_pool_statistics(&op, ifs);
461		break;
462
463	case IPLT_HASH :
464		err = fr_gethtablestat(&op, ifs);
465		break;
466
467	default :
468		err = EINVAL;
469		break;
470	}
471	return err;
472}
473
474
475/* ------------------------------------------------------------------------ */
476/* Function:    iplookup_flush                                              */
477/* Returns:     int     - 0 = success, else error                           */
478/* Parameters:  data(I) - pointer to data from ioctl call                   */
479/*                                                                          */
480/* A flush is called when we want to flush all the nodes from a particular  */
481/* entry in the hash table/pool or want to remove all groups from those.    */
482/* ------------------------------------------------------------------------ */
483static int iplookup_flush(data, ifs)
484caddr_t data;
485ipf_stack_t *ifs;
486{
487	int err, unit, num, type;
488	iplookupflush_t flush;
489
490	err = BCOPYIN(data, &flush, sizeof(flush));
491	if (err != 0)
492		return EFAULT;
493
494	flush.iplf_name[sizeof(flush.iplf_name) - 1] = '\0';
495
496	unit = flush.iplf_unit;
497	if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL))
498		return EINVAL;
499
500	type = flush.iplf_type;
501	err = EINVAL;
502	num = 0;
503
504	if (type == IPLT_POOL || type == IPLT_ALL) {
505		err = 0;
506		num = ip_pool_flush(&flush, ifs);
507	}
508
509	if (type == IPLT_HASH  || type == IPLT_ALL) {
510		err = 0;
511		num += fr_flushhtable(&flush, ifs);
512	}
513
514	if (err == 0) {
515		flush.iplf_count = num;
516		err = COPYOUT(&flush, data, sizeof(flush));
517	}
518	return err;
519}
520
521
522
523void ip_lookup_deref(type, ptr, ifs)
524int type;
525void *ptr;
526ipf_stack_t *ifs;
527{
528	if (ptr == NULL)
529		return;
530
531	WRITE_ENTER(&ifs->ifs_ip_poolrw);
532	switch (type)
533	{
534	case IPLT_POOL :
535		ip_pool_deref(ptr, ifs);
536		break;
537
538	case IPLT_HASH :
539		fr_derefhtable(ptr, ifs);
540		break;
541	}
542	RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
543}
544
545
546int ip_lookup_iterate(data, uid, ctx, ifs)
547void *data;
548int uid;
549void *ctx;
550ipf_stack_t *ifs;
551{
552	ipflookupiter_t iter;
553	ipftoken_t *token;
554	int err;
555
556	err = fr_inobj(data, &iter, IPFOBJ_LOOKUPITER);
557	if (err != 0) {
558#ifdef _KERNEL
559		(void) printf("fr_inobj\n");
560#endif
561		return err;
562	}
563
564	if (iter.ili_unit < 0 || iter.ili_unit > IPL_LOGMAX) {
565#ifdef _KERNEL
566		(void) printf("unit=%d\n", iter.ili_unit);
567#endif
568		return EINVAL;
569	}
570
571	if (iter.ili_ival != IPFGENITER_LOOKUP) {
572#ifdef _KERNEL
573		(void) printf("ival=%d\n", iter.ili_ival);
574#endif
575		return EINVAL;
576	}
577
578	token = ipf_findtoken(iter.ili_key, uid, ctx, ifs);
579	if (token == NULL) {
580		RWLOCK_EXIT(&ifs->ifs_ipf_tokens);
581		return ESRCH;
582	}
583
584	switch (iter.ili_type)
585	{
586	case IPLT_POOL :
587		err = ip_pool_getnext(token, &iter, ifs);
588		break;
589	case IPLT_HASH :
590		err = fr_htable_getnext(token, &iter, ifs);
591		break;
592	default :
593#ifdef _KERNEL
594		(void) printf("type=%d\n", iter.ili_type);
595#endif
596		err = EINVAL;
597		break;
598	}
599	RWLOCK_EXIT(&ifs->ifs_ipf_tokens);
600
601	return err;
602}
603
604
605void ip_lookup_iterderef(type, data, ifs)
606u_32_t type;
607void *data;
608ipf_stack_t *ifs;
609{
610	iplookupiterkey_t	key;
611
612	key.ilik_key = type;
613
614	if (key.ilik_unstr.ilik_ival != IPFGENITER_LOOKUP)
615		return;
616
617	switch (key.ilik_unstr.ilik_type)
618	{
619	case IPLT_HASH :
620		fr_htable_iterderef((u_int)key.ilik_unstr.ilik_otype,
621				    (int)key.ilik_unstr.ilik_unit, data, ifs);
622		break;
623	case IPLT_POOL :
624		ip_pool_iterderef((u_int)key.ilik_unstr.ilik_otype,
625				  (int)key.ilik_unstr.ilik_unit, data, ifs);
626		break;
627	}
628}
629
630
631#else /* IPFILTER_LOOKUP */
632
633/*ARGSUSED*/
634int ip_lookup_ioctl(data, cmd, mode, uid, ifs)
635caddr_t data;
636ioctlcmd_t cmd;
637int mode, uid;
638ipf_stack_t *ifs;
639{
640	return EIO;
641}
642#endif /* IPFILTER_LOOKUP */
643