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__
30 struct 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)
61 static 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
65 int	ip_lookup_inited = 0;
66 
67 static int iplookup_addnode __P((caddr_t));
68 static int iplookup_delnode __P((caddr_t data));
69 static int iplookup_addtable __P((caddr_t));
70 static int iplookup_deltable __P((caddr_t));
71 static int iplookup_stats __P((caddr_t));
72 static int iplookup_flush __P((caddr_t));
73 static int iplookup_iterate __P((void *, int, void *));
74 static 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 /* ------------------------------------------------------------------------ */
ip_lookup_init()84 int 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 /* ------------------------------------------------------------------------ */
ip_lookup_unload()107 void 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 /* ------------------------------------------------------------------------ */
ip_lookup_ioctl(data,cmd,mode,uid,ctx)131 int ip_lookup_ioctl(data, cmd, mode, uid, ctx)
132 caddr_t data;
133 ioctlcmd_t cmd;
134 int mode, uid;
135 void *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 /* ------------------------------------------------------------------------ */
iplookup_addnode(data)211 static int iplookup_addnode(data)
212 caddr_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 /* ------------------------------------------------------------------------ */
iplookup_delnode(data)286 static int iplookup_delnode(data)
287 caddr_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 /* ------------------------------------------------------------------------ */
iplookup_addtable(data)355 static int iplookup_addtable(data)
356 caddr_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 /* ------------------------------------------------------------------------ */
iplookup_deltable(data)413 static int iplookup_deltable(data)
414 caddr_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 /* ------------------------------------------------------------------------ */
iplookup_stats(data)457 static int iplookup_stats(data)
458 caddr_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 /* ------------------------------------------------------------------------ */
iplookup_flush(data)496 static int iplookup_flush(data)
497 caddr_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 /* ------------------------------------------------------------------------ */
ip_lookup_deref(type,ptr)545 void ip_lookup_deref(type, ptr)
546 int type;
547 void *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 /* ------------------------------------------------------------------------ */
iplookup_iterate(data,uid,ctx)576 static int iplookup_iterate(data, uid, ctx)
577 void *data;
578 int uid;
579 void *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 /* ------------------------------------------------------------------------ */
ip_lookup_iterderef(type,data)631 void ip_lookup_iterderef(type, data)
632 u_32_t type;
633 void *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 /* ------------------------------------------------------------------------ */
iplookup_deltok(data,uid,ctx)667 static int iplookup_deltok(data, uid, ctx)
668 void *data;
669 int uid;
670 void *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*/
ip_lookup_ioctl(data,cmd,mode,uid,ctx)687 int ip_lookup_ioctl(data, cmd, mode, uid, ctx)
688 caddr_t data;
689 ioctlcmd_t cmd;
690 int mode, uid;
691 void *ctx;
692 {
693 	return EIO;
694 }
695 #endif /* IPFILTER_LOOKUP */
696