1 /*
2  * Copyright (C) 2002-2003 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * Copyright 2006 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 #if defined(__osf__)
19 # define _PROTO_NET_H_
20 #endif
21 #include <sys/param.h>
22 #include <sys/errno.h>
23 #include <sys/types.h>
24 #include <sys/time.h>
25 #include <sys/file.h>
26 #if __FreeBSD_version >= 220000 && defined(_KERNEL)
27 # include <sys/fcntl.h>
28 # include <sys/filio.h>
29 #else
30 # include <sys/ioctl.h>
31 #endif
32 #if !defined(_KERNEL)
33 # include <string.h>
34 # define _KERNEL
35 # ifdef __OpenBSD__
36 struct file;
37 # endif
38 # include <sys/uio.h>
39 # undef _KERNEL
40 #endif
41 #include <sys/socket.h>
42 #if (defined(__osf__) || defined(AIX) || defined(__hpux) || defined(__sgi)) && defined(_KERNEL)
43 # ifdef __osf__
44 #  include <net/radix.h>
45 # endif
46 # include "radix_ipf_local.h"
47 # define _RADIX_H_
48 #endif
49 #include <net/if.h>
50 #if defined(__FreeBSD__)
51 #  include <sys/cdefs.h>
52 #  include <sys/proc.h>
53 #endif
54 #if defined(_KERNEL)
55 # include <sys/systm.h>
56 # if !defined(__SVR4) && !defined(__svr4__)
57 #  include <sys/mbuf.h>
58 # endif
59 #endif
60 #include <netinet/in.h>
61 
62 #include "netinet/ip_compat.h"
63 #include "netinet/ip_fil.h"
64 #include "netinet/ip_pool.h"
65 #include "netinet/ip_htable.h"
66 #include "netinet/ip_lookup.h"
67 /* END OF INCLUDES */
68 
69 #if !defined(lint)
70 static const char rcsid[] = "@(#)$Id: ip_lookup.c,v 2.35.2.7 2005/06/12 07:18:20 darrenr Exp $";
71 #endif
72 
73 #ifdef	IPFILTER_LOOKUP
74 int	ip_lookup_inited = 0;
75 
76 static int iplookup_addnode __P((caddr_t));
77 static int iplookup_delnode __P((caddr_t data));
78 static int iplookup_addtable __P((caddr_t));
79 static int iplookup_deltable __P((caddr_t));
80 static int iplookup_stats __P((caddr_t));
81 static int iplookup_flush __P((caddr_t));
82 
83 
84 /* ------------------------------------------------------------------------ */
85 /* Function:    iplookup_init                                               */
86 /* Returns:     int     - 0 = success, else error                           */
87 /* Parameters:  Nil                                                         */
88 /*                                                                          */
89 /* Initialise all of the subcomponents of the lookup infrstructure.         */
90 /* ------------------------------------------------------------------------ */
91 int ip_lookup_init()
92 {
93 
94 	if (ip_pool_init() == -1)
95 		return -1;
96 
97 	RWLOCK_INIT(&ip_poolrw, "ip pool rwlock");
98 
99 	ip_lookup_inited = 1;
100 
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 /* ------------------------------------------------------------------------ */
114 void ip_lookup_unload()
115 {
116 	ip_pool_fini();
117 	fr_htable_unload();
118 
119 	if (ip_lookup_inited == 1) {
120 		RW_DESTROY(&ip_poolrw);
121 		ip_lookup_inited = 0;
122 	}
123 }
124 
125 
126 /* ------------------------------------------------------------------------ */
127 /* Function:    iplookup_ioctl                                              */
128 /* Returns:     int      - 0 = success, else error                          */
129 /* Parameters:  data(IO) - pointer to ioctl data to be copied to/from user  */
130 /*                         space.                                           */
131 /*              cmd(I)   - ioctl command number                             */
132 /*              mode(I)  - file mode bits used with open                    */
133 /*                                                                          */
134 /* Handle ioctl commands sent to the ioctl device.  For the most part, this */
135 /* involves just calling another function to handle the specifics of each   */
136 /* command.                                                                 */
137 /* ------------------------------------------------------------------------ */
138 int ip_lookup_ioctl(data, cmd, mode)
139 caddr_t data;
140 ioctlcmd_t cmd;
141 int mode;
142 {
143 	int err;
144 	SPL_INT(s);
145 
146 	mode = mode;	/* LINT */
147 
148 	SPL_NET(s);
149 
150 	switch (cmd)
151 	{
152 	case SIOCLOOKUPADDNODE :
153 	case SIOCLOOKUPADDNODEW :
154 		WRITE_ENTER(&ip_poolrw);
155 		err = iplookup_addnode(data);
156 		RWLOCK_EXIT(&ip_poolrw);
157 		break;
158 
159 	case SIOCLOOKUPDELNODE :
160 	case SIOCLOOKUPDELNODEW :
161 		WRITE_ENTER(&ip_poolrw);
162 		err = iplookup_delnode(data);
163 		RWLOCK_EXIT(&ip_poolrw);
164 		break;
165 
166 	case SIOCLOOKUPADDTABLE :
167 		WRITE_ENTER(&ip_poolrw);
168 		err = iplookup_addtable(data);
169 		RWLOCK_EXIT(&ip_poolrw);
170 		break;
171 
172 	case SIOCLOOKUPDELTABLE :
173 		WRITE_ENTER(&ip_poolrw);
174 		err = iplookup_deltable(data);
175 		RWLOCK_EXIT(&ip_poolrw);
176 		break;
177 
178 	case SIOCLOOKUPSTAT :
179 	case SIOCLOOKUPSTATW :
180 		WRITE_ENTER(&ip_poolrw);
181 		err = iplookup_stats(data);
182 		RWLOCK_EXIT(&ip_poolrw);
183 		break;
184 
185 	case SIOCLOOKUPFLUSH :
186 		WRITE_ENTER(&ip_poolrw);
187 		err = iplookup_flush(data);
188 		RWLOCK_EXIT(&ip_poolrw);
189 		break;
190 
191 	default :
192 		err = EINVAL;
193 		break;
194 	}
195 	SPL_X(s);
196 	return err;
197 }
198 
199 
200 /* ------------------------------------------------------------------------ */
201 /* Function:    iplookup_addnode                                            */
202 /* Returns:     int     - 0 = success, else error                           */
203 /* Parameters:  data(I) - pointer to data from ioctl call                   */
204 /*                                                                          */
205 /* Add a new data node to a lookup structure.  First, check to see if the   */
206 /* parent structure refered to by name exists and if it does, then go on to */
207 /* add a node to it.                                                        */
208 /* ------------------------------------------------------------------------ */
209 static int iplookup_addnode(data)
210 caddr_t data;
211 {
212 	ip_pool_node_t node, *m;
213 	iplookupop_t op;
214 	iphtable_t *iph;
215 	iphtent_t hte;
216 	ip_pool_t *p;
217 	int err;
218 
219 	err = 0;
220 	BCOPYIN(data, &op, sizeof(op));
221 	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
222 
223 	switch (op.iplo_type)
224 	{
225 	case IPLT_POOL :
226 		if (op.iplo_size != sizeof(node))
227 			return EINVAL;
228 
229 		err = COPYIN(op.iplo_struct, &node, sizeof(node));
230 		if (err != 0)
231 			return EFAULT;
232 
233 		p = ip_pool_find(op.iplo_unit, op.iplo_name);
234 		if (p == NULL)
235 			return ESRCH;
236 
237 		/*
238 		 * add an entry to a pool - return an error if it already
239 		 * exists remove an entry from a pool - if it exists
240 		 * - in both cases, the pool *must* exist!
241 		 */
242 		m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask);
243 		if (m)
244 			return EEXIST;
245 		err = ip_pool_insert(p, &node.ipn_addr,
246 				     &node.ipn_mask, node.ipn_info);
247 		break;
248 
249 	case IPLT_HASH :
250 		if (op.iplo_size != sizeof(hte))
251 			return EINVAL;
252 
253 		err = COPYIN(op.iplo_struct, &hte, sizeof(hte));
254 		if (err != 0)
255 			return EFAULT;
256 
257 		iph = fr_findhtable(op.iplo_unit, op.iplo_name);
258 		if (iph == NULL)
259 			return ESRCH;
260 		err = fr_addhtent(iph, &hte);
261 		break;
262 
263 	default :
264 		err = EINVAL;
265 		break;
266 	}
267 	return err;
268 }
269 
270 
271 /* ------------------------------------------------------------------------ */
272 /* Function:    iplookup_delnode                                            */
273 /* Returns:     int     - 0 = success, else error                           */
274 /* Parameters:  data(I) - pointer to data from ioctl call                   */
275 /*                                                                          */
276 /* Delete a node from a lookup table by first looking for the table it is   */
277 /* in and then deleting the entry that gets found.                          */
278 /* ------------------------------------------------------------------------ */
279 static int iplookup_delnode(data)
280 caddr_t data;
281 {
282 	ip_pool_node_t node, *m;
283 	iplookupop_t op;
284 	iphtable_t *iph;
285 	iphtent_t hte;
286 	ip_pool_t *p;
287 	int err;
288 
289 	err = 0;
290 	BCOPYIN(data, &op, sizeof(op));
291 
292 	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
293 
294 	switch (op.iplo_type)
295 	{
296 	case IPLT_POOL :
297 		if (op.iplo_size != sizeof(node))
298 			return EINVAL;
299 
300 		err = COPYIN(op.iplo_struct, &node, sizeof(node));
301 		if (err != 0)
302 			return EFAULT;
303 
304 		p = ip_pool_find(op.iplo_unit, op.iplo_name);
305 		if (!p)
306 			return ESRCH;
307 
308 		m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask);
309 		if (m == NULL)
310 			return ENOENT;
311 		err = ip_pool_remove(p, m);
312 		break;
313 
314 	case IPLT_HASH :
315 		if (op.iplo_size != sizeof(hte))
316 			return EINVAL;
317 
318 		err = COPYIN(op.iplo_struct, &hte, sizeof(hte));
319 		if (err != 0)
320 			return EFAULT;
321 
322 		iph = fr_findhtable(op.iplo_unit, op.iplo_name);
323 		if (iph == NULL)
324 			return ESRCH;
325 		err = fr_delhtent(iph, &hte);
326 		break;
327 
328 	default :
329 		err = EINVAL;
330 		break;
331 	}
332 	return err;
333 }
334 
335 
336 /* ------------------------------------------------------------------------ */
337 /* Function:    iplookup_addtable                                           */
338 /* Returns:     int     - 0 = success, else error                           */
339 /* Parameters:  data(I) - pointer to data from ioctl call                   */
340 /*                                                                          */
341 /* Create a new lookup table, if one doesn't already exist using the name   */
342 /* for this one.                                                            */
343 /* ------------------------------------------------------------------------ */
344 static int iplookup_addtable(data)
345 caddr_t data;
346 {
347 	iplookupop_t op;
348 	int err;
349 
350 	err = 0;
351 	BCOPYIN(data, &op, sizeof(op));
352 
353 	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
354 
355 	switch (op.iplo_type)
356 	{
357 	case IPLT_POOL :
358 		if (ip_pool_find(op.iplo_unit, op.iplo_name) != NULL)
359 			err = EEXIST;
360 		else
361 			err = ip_pool_create(&op);
362 		break;
363 
364 	case IPLT_HASH :
365 		if (fr_findhtable(op.iplo_unit, op.iplo_name) != NULL)
366 			err = EEXIST;
367 		else
368 			err = fr_newhtable(&op);
369 		break;
370 
371 	default :
372 		err = EINVAL;
373 		break;
374 	}
375 	return err;
376 }
377 
378 
379 /* ------------------------------------------------------------------------ */
380 /* Function:    iplookup_deltable                                           */
381 /* Returns:     int     - 0 = success, else error                           */
382 /* Parameters:  data(I) - pointer to data from ioctl call                   */
383 /*                                                                          */
384 /* Decodes ioctl request to remove a particular hash table or pool and      */
385 /* calls the relevant function to do the cleanup.                           */
386 /* ------------------------------------------------------------------------ */
387 static int iplookup_deltable(data)
388 caddr_t data;
389 {
390 	iplookupop_t op;
391 	int err;
392 
393 	BCOPYIN(data, &op, sizeof(op));
394 	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
395 
396 	if (op.iplo_arg & IPLT_ANON)
397 		op.iplo_arg &= IPLT_ANON;
398 
399 	/*
400 	 * create a new pool - fail if one already exists with
401 	 * the same #
402 	 */
403 	switch (op.iplo_type)
404 	{
405 	case IPLT_POOL :
406 		err = ip_pool_destroy(&op);
407 		break;
408 
409 	case IPLT_HASH :
410 		err = fr_removehtable(&op);
411 		break;
412 
413 	default :
414 		err = EINVAL;
415 		break;
416 	}
417 	return err;
418 }
419 
420 
421 /* ------------------------------------------------------------------------ */
422 /* Function:    iplookup_stats                                              */
423 /* Returns:     int     - 0 = success, else error                           */
424 /* Parameters:  data(I) - pointer to data from ioctl call                   */
425 /*                                                                          */
426 /* Copy statistical information from inside the kernel back to user space.  */
427 /* ------------------------------------------------------------------------ */
428 static int iplookup_stats(data)
429 caddr_t data;
430 {
431 	iplookupop_t op;
432 	int err;
433 
434 	err = 0;
435 	BCOPYIN(data, &op, sizeof(op));
436 
437 	switch (op.iplo_type)
438 	{
439 	case IPLT_POOL :
440 		err = ip_pool_statistics(&op);
441 		break;
442 
443 	case IPLT_HASH :
444 		err = fr_gethtablestat(&op);
445 		break;
446 
447 	default :
448 		err = EINVAL;
449 		break;
450 	}
451 	return err;
452 }
453 
454 
455 /* ------------------------------------------------------------------------ */
456 /* Function:    iplookup_flush                                              */
457 /* Returns:     int     - 0 = success, else error                           */
458 /* Parameters:  data(I) - pointer to data from ioctl call                   */
459 /*                                                                          */
460 /* A flush is called when we want to flush all the nodes from a particular  */
461 /* entry in the hash table/pool or want to remove all groups from those.    */
462 /* ------------------------------------------------------------------------ */
463 static int iplookup_flush(data)
464 caddr_t data;
465 {
466 	int err, unit, num, type;
467 	iplookupflush_t flush;
468 
469 	err = 0;
470 	BCOPYIN(data, &flush, sizeof(flush));
471 
472 	flush.iplf_name[sizeof(flush.iplf_name) - 1] = '\0';
473 
474 	unit = flush.iplf_unit;
475 	if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL))
476 		return EINVAL;
477 
478 	type = flush.iplf_type;
479 	err = EINVAL;
480 	num = 0;
481 
482 	if (type == IPLT_POOL || type == IPLT_ALL) {
483 		err = 0;
484 		num = ip_pool_flush(&flush);
485 	}
486 
487 	if (type == IPLT_HASH  || type == IPLT_ALL) {
488 		err = 0;
489 		num += fr_flushhtable(&flush);
490 	}
491 
492 	if (err == 0) {
493 		flush.iplf_count = num;
494 		err = COPYOUT(&flush, data, sizeof(flush));
495 	}
496 	return err;
497 }
498 
499 
500 void ip_lookup_deref(type, ptr)
501 int type;
502 void *ptr;
503 {
504 	if (ptr == NULL)
505 		return;
506 
507 	WRITE_ENTER(&ip_poolrw);
508 	switch (type)
509 	{
510 	case IPLT_POOL :
511 		ip_pool_deref(ptr);
512 		break;
513 
514 	case IPLT_HASH :
515 		fr_derefhtable(ptr);
516 		break;
517 	}
518 	RWLOCK_EXIT(&ip_poolrw);
519 }
520 
521 
522 #else /* IPFILTER_LOOKUP */
523 
524 /*ARGSUSED*/
525 int ip_lookup_ioctl(data, cmd, mode)
526 caddr_t data;
527 ioctlcmd_t cmd;
528 int mode;
529 {
530 	return EIO;
531 }
532 #endif /* IPFILTER_LOOKUP */
533