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