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__
34 struct 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)
69 static 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
73 static int iplookup_addnode __P((caddr_t, ipf_stack_t *));
74 static int iplookup_delnode __P((caddr_t data, ipf_stack_t *));
75 static int iplookup_addtable __P((caddr_t, ipf_stack_t *));
76 static int iplookup_deltable __P((caddr_t, ipf_stack_t *));
77 static int iplookup_stats __P((caddr_t, ipf_stack_t *));
78 static 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 /* ------------------------------------------------------------------------ */
ip_lookup_init(ifs)89 int ip_lookup_init(ifs)
90 ipf_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 /* ------------------------------------------------------------------------ */
ip_lookup_unload(ifs)114 void ip_lookup_unload(ifs)
115 ipf_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 /* ------------------------------------------------------------------------ */
ip_lookup_ioctl(data,cmd,mode,uid,ctx,ifs)139 int ip_lookup_ioctl(data, cmd, mode, uid, ctx, ifs)
140 caddr_t data;
141 ioctlcmd_t cmd;
142 int mode, uid;
143 void *ctx;
144 ipf_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 /* ------------------------------------------------------------------------ */
iplookup_addnode(data,ifs)216 static int iplookup_addnode(data, ifs)
217 caddr_t data;
218 ipf_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 /* ------------------------------------------------------------------------ */
iplookup_delnode(data,ifs)289 static int iplookup_delnode(data, ifs)
290 caddr_t data;
291 ipf_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 /* ------------------------------------------------------------------------ */
iplookup_addtable(data,ifs)356 static int iplookup_addtable(data, ifs)
357 caddr_t data;
358 ipf_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 /* ------------------------------------------------------------------------ */
iplookup_deltable(data,ifs)401 static int iplookup_deltable(data, ifs)
402 caddr_t data;
403 ipf_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 /* ------------------------------------------------------------------------ */
iplookup_stats(data,ifs)446 static int iplookup_stats(data, ifs)
447 caddr_t data;
448 ipf_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 /* ------------------------------------------------------------------------ */
iplookup_flush(data,ifs)483 static int iplookup_flush(data, ifs)
484 caddr_t data;
485 ipf_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 
ip_lookup_deref(type,ptr,ifs)523 void ip_lookup_deref(type, ptr, ifs)
524 int type;
525 void *ptr;
526 ipf_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 
ip_lookup_iterate(data,uid,ctx,ifs)546 int ip_lookup_iterate(data, uid, ctx, ifs)
547 void *data;
548 int uid;
549 void *ctx;
550 ipf_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 
ip_lookup_iterderef(type,data,ifs)605 void ip_lookup_iterderef(type, data, ifs)
606 u_32_t type;
607 void *data;
608 ipf_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*/
ip_lookup_ioctl(data,cmd,mode,uid,ifs)634 int ip_lookup_ioctl(data, cmd, mode, uid, ifs)
635 caddr_t data;
636 ioctlcmd_t cmd;
637 int mode, uid;
638 ipf_stack_t *ifs;
639 {
640 	return EIO;
641 }
642 #endif /* IPFILTER_LOOKUP */
643