xref: /illumos-gate/usr/src/uts/common/inet/ipf/ip_lookup.c (revision b0fe7b8fa79924061f3bdf7f240ea116c2c0b704)
1 /*
2  * Copyright (C) 2002-2003 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  *
6  * Copyright 2007 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 #include "netinet/ipf_stack.h"
68 /* END OF INCLUDES */
69 
70 #if !defined(lint)
71 static const char rcsid[] = "@(#)$Id: ip_lookup.c,v 2.35.2.7 2005/06/12 07:18:20 darrenr Exp $";
72 #endif
73 
74 #ifdef	IPFILTER_LOOKUP
75 static int iplookup_addnode __P((caddr_t, ipf_stack_t *));
76 static int iplookup_delnode __P((caddr_t data, ipf_stack_t *));
77 static int iplookup_addtable __P((caddr_t, ipf_stack_t *));
78 static int iplookup_deltable __P((caddr_t, ipf_stack_t *));
79 static int iplookup_stats __P((caddr_t, ipf_stack_t *));
80 static int iplookup_flush __P((caddr_t, ipf_stack_t *));
81 
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(ifs)
92 ipf_stack_t *ifs;
93 {
94 
95 	if (ip_pool_init(ifs) == -1)
96 		return -1;
97 
98 	RWLOCK_INIT(&ifs->ifs_ip_poolrw, "ip pool rwlock");
99 
100 	ifs->ifs_ip_lookup_inited = 1;
101 	ifs->ifs_ipftokenhead = NULL;
102 	ifs->ifs_ipftokentail = &ifs->ifs_ipftokenhead;
103 	return 0;
104 }
105 
106 
107 /* ------------------------------------------------------------------------ */
108 /* Function:    iplookup_unload                                             */
109 /* Returns:     int     - 0 = success, else error                           */
110 /* Parameters:  Nil                                                         */
111 /*                                                                          */
112 /* Free up all pool related memory that has been allocated whilst IPFilter  */
113 /* has been running.  Also, do any other deinitialisation required such     */
114 /* ip_lookup_init() can be called again, safely.                            */
115 /* ------------------------------------------------------------------------ */
116 void ip_lookup_unload(ifs)
117 ipf_stack_t *ifs;
118 {
119 	ip_pool_fini(ifs);
120 	fr_htable_unload(ifs);
121 
122 	if (ifs->ifs_ip_lookup_inited == 1) {
123 		RW_DESTROY(&ifs->ifs_ip_poolrw);
124 		ifs->ifs_ip_lookup_inited = 0;
125 	}
126 }
127 
128 
129 /* ------------------------------------------------------------------------ */
130 /* Function:    iplookup_ioctl                                              */
131 /* Returns:     int      - 0 = success, else error                          */
132 /* Parameters:  data(IO) - pointer to ioctl data to be copied to/from user  */
133 /*                         space.                                           */
134 /*              cmd(I)   - ioctl command number                             */
135 /*              mode(I)  - file mode bits used with open                    */
136 /*                                                                          */
137 /* Handle ioctl commands sent to the ioctl device.  For the most part, this */
138 /* involves just calling another function to handle the specifics of each   */
139 /* command.                                                                 */
140 /* ------------------------------------------------------------------------ */
141 int ip_lookup_ioctl(data, cmd, mode, uid, ctx, ifs)
142 caddr_t data;
143 ioctlcmd_t cmd;
144 int mode, uid;
145 void *ctx;
146 ipf_stack_t *ifs;
147 {
148 	int err;
149 	SPL_INT(s);
150 
151 	mode = mode;	/* LINT */
152 
153 	SPL_NET(s);
154 
155 	switch (cmd)
156 	{
157 	case SIOCLOOKUPADDNODE :
158 	case SIOCLOOKUPADDNODEW :
159 		WRITE_ENTER(&ifs->ifs_ip_poolrw);
160 		err = iplookup_addnode(data, ifs);
161 		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
162 		break;
163 
164 	case SIOCLOOKUPDELNODE :
165 	case SIOCLOOKUPDELNODEW :
166 		WRITE_ENTER(&ifs->ifs_ip_poolrw);
167 		err = iplookup_delnode(data, ifs);
168 		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
169 		break;
170 
171 	case SIOCLOOKUPADDTABLE :
172 		WRITE_ENTER(&ifs->ifs_ip_poolrw);
173 		err = iplookup_addtable(data, ifs);
174 		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
175 		break;
176 
177 	case SIOCLOOKUPDELTABLE :
178 		WRITE_ENTER(&ifs->ifs_ip_poolrw);
179 		err = iplookup_deltable(data, ifs);
180 		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
181 		break;
182 
183 	case SIOCLOOKUPSTAT :
184 	case SIOCLOOKUPSTATW :
185 		WRITE_ENTER(&ifs->ifs_ip_poolrw);
186 		err = iplookup_stats(data, ifs);
187 		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
188 		break;
189 
190 	case SIOCLOOKUPFLUSH :
191 		WRITE_ENTER(&ifs->ifs_ip_poolrw);
192 		err = iplookup_flush(data, ifs);
193 		RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
194 		break;
195 
196 	case SIOCLOOKUPITER :
197 		err = ip_lookup_iterate(data, uid, ctx, ifs);
198 		break;
199 
200 	default :
201 		err = EINVAL;
202 		break;
203 	}
204 	SPL_X(s);
205 	return err;
206 }
207 
208 
209 /* ------------------------------------------------------------------------ */
210 /* Function:    iplookup_addnode                                            */
211 /* Returns:     int     - 0 = success, else error                           */
212 /* Parameters:  data(I) - pointer to data from ioctl call                   */
213 /*                                                                          */
214 /* Add a new data node to a lookup structure.  First, check to see if the   */
215 /* parent structure refered to by name exists and if it does, then go on to */
216 /* add a node to it.                                                        */
217 /* ------------------------------------------------------------------------ */
218 static int iplookup_addnode(data, ifs)
219 caddr_t data;
220 ipf_stack_t *ifs;
221 {
222 	ip_pool_node_t node, *m;
223 	iplookupop_t op;
224 	iphtable_t *iph;
225 	iphtent_t hte;
226 	ip_pool_t *p;
227 	int err;
228 
229 	err = 0;
230 	BCOPYIN(data, &op, sizeof(op));
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 /* ------------------------------------------------------------------------ */
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 = 0;
301 	BCOPYIN(data, &op, sizeof(op));
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, ifs);
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, ifs);
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, ifs);
334 		if (iph == NULL)
335 			return ESRCH;
336 		err = fr_delhtent(iph, &hte, ifs);
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 /* ------------------------------------------------------------------------ */
355 static int iplookup_addtable(data, ifs)
356 caddr_t data;
357 ipf_stack_t *ifs;
358 {
359 	iplookupop_t op;
360 	int err;
361 
362 	err = 0;
363 	BCOPYIN(data, &op, sizeof(op));
364 
365 	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
366 
367 	switch (op.iplo_type)
368 	{
369 	case IPLT_POOL :
370 		if (ip_pool_find(op.iplo_unit, op.iplo_name, ifs) != NULL)
371 			err = EEXIST;
372 		else
373 			err = ip_pool_create(&op, ifs);
374 		break;
375 
376 	case IPLT_HASH :
377 		if (fr_findhtable(op.iplo_unit, op.iplo_name, ifs) != NULL)
378 			err = EEXIST;
379 		else
380 			err = fr_newhtable(&op, ifs);
381 		break;
382 
383 	default :
384 		err = EINVAL;
385 		break;
386 	}
387 	return err;
388 }
389 
390 
391 /* ------------------------------------------------------------------------ */
392 /* Function:    iplookup_deltable                                           */
393 /* Returns:     int     - 0 = success, else error                           */
394 /* Parameters:  data(I) - pointer to data from ioctl call                   */
395 /*                                                                          */
396 /* Decodes ioctl request to remove a particular hash table or pool and      */
397 /* calls the relevant function to do the cleanup.                           */
398 /* ------------------------------------------------------------------------ */
399 static int iplookup_deltable(data, ifs)
400 caddr_t data;
401 ipf_stack_t *ifs;
402 {
403 	iplookupop_t op;
404 	int err;
405 
406 	BCOPYIN(data, &op, sizeof(op));
407 	op.iplo_name[sizeof(op.iplo_name) - 1] = '\0';
408 
409 	if (op.iplo_arg & IPLT_ANON)
410 		op.iplo_arg &= IPLT_ANON;
411 
412 	/*
413 	 * create a new pool - fail if one already exists with
414 	 * the same #
415 	 */
416 	switch (op.iplo_type)
417 	{
418 	case IPLT_POOL :
419 		err = ip_pool_destroy(&op, ifs);
420 		break;
421 
422 	case IPLT_HASH :
423 		err = fr_removehtable(&op, ifs);
424 		break;
425 
426 	default :
427 		err = EINVAL;
428 		break;
429 	}
430 	return err;
431 }
432 
433 
434 /* ------------------------------------------------------------------------ */
435 /* Function:    iplookup_stats                                              */
436 /* Returns:     int     - 0 = success, else error                           */
437 /* Parameters:  data(I) - pointer to data from ioctl call                   */
438 /*                                                                          */
439 /* Copy statistical information from inside the kernel back to user space.  */
440 /* ------------------------------------------------------------------------ */
441 static int iplookup_stats(data, ifs)
442 caddr_t data;
443 ipf_stack_t *ifs;
444 {
445 	iplookupop_t op;
446 	int err;
447 
448 	err = 0;
449 	BCOPYIN(data, &op, sizeof(op));
450 
451 	switch (op.iplo_type)
452 	{
453 	case IPLT_POOL :
454 		err = ip_pool_statistics(&op, ifs);
455 		break;
456 
457 	case IPLT_HASH :
458 		err = fr_gethtablestat(&op, ifs);
459 		break;
460 
461 	default :
462 		err = EINVAL;
463 		break;
464 	}
465 	return err;
466 }
467 
468 
469 /* ------------------------------------------------------------------------ */
470 /* Function:    iplookup_flush                                              */
471 /* Returns:     int     - 0 = success, else error                           */
472 /* Parameters:  data(I) - pointer to data from ioctl call                   */
473 /*                                                                          */
474 /* A flush is called when we want to flush all the nodes from a particular  */
475 /* entry in the hash table/pool or want to remove all groups from those.    */
476 /* ------------------------------------------------------------------------ */
477 static int iplookup_flush(data, ifs)
478 caddr_t data;
479 ipf_stack_t *ifs;
480 {
481 	int err, unit, num, type;
482 	iplookupflush_t flush;
483 
484 	err = 0;
485 	BCOPYIN(data, &flush, sizeof(flush));
486 
487 	flush.iplf_name[sizeof(flush.iplf_name) - 1] = '\0';
488 
489 	unit = flush.iplf_unit;
490 	if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL))
491 		return EINVAL;
492 
493 	type = flush.iplf_type;
494 	err = EINVAL;
495 	num = 0;
496 
497 	if (type == IPLT_POOL || type == IPLT_ALL) {
498 		err = 0;
499 		num = ip_pool_flush(&flush, ifs);
500 	}
501 
502 	if (type == IPLT_HASH  || type == IPLT_ALL) {
503 		err = 0;
504 		num += fr_flushhtable(&flush, ifs);
505 	}
506 
507 	if (err == 0) {
508 		flush.iplf_count = num;
509 		err = COPYOUT(&flush, data, sizeof(flush));
510 	}
511 	return err;
512 }
513 
514 
515 
516 void ip_lookup_deref(type, ptr, ifs)
517 int type;
518 void *ptr;
519 ipf_stack_t *ifs;
520 {
521 	if (ptr == NULL)
522 		return;
523 
524 	WRITE_ENTER(&ifs->ifs_ip_poolrw);
525 	switch (type)
526 	{
527 	case IPLT_POOL :
528 		ip_pool_deref(ptr, ifs);
529 		break;
530 
531 	case IPLT_HASH :
532 		fr_derefhtable(ptr, ifs);
533 		break;
534 	}
535 	RWLOCK_EXIT(&ifs->ifs_ip_poolrw);
536 }
537 
538 
539 int ip_lookup_iterate(data, uid, ctx, ifs)
540 void *data;
541 int uid;
542 void *ctx;
543 ipf_stack_t *ifs;
544 {
545 	ipflookupiter_t iter;
546 	ipftoken_t *token;
547 	int err;
548 
549 	err = fr_inobj(data, &iter, IPFOBJ_LOOKUPITER);
550 	if (err != 0) {
551 #ifdef _KERNEL
552 		(void) printf("fr_inobj\n");
553 #endif
554 		return err;
555 	}
556 
557 	if (iter.ili_unit < 0 || iter.ili_unit > IPL_LOGMAX) {
558 #ifdef _KERNEL
559 		(void) printf("unit=%d\n", iter.ili_unit);
560 #endif
561 		return EINVAL;
562 	}
563 
564 	if (iter.ili_ival != IPFGENITER_LOOKUP) {
565 #ifdef _KERNEL
566 		(void) printf("ival=%d\n", iter.ili_ival);
567 #endif
568 		return EINVAL;
569 	}
570 
571 	token = ipf_findtoken(iter.ili_key, uid, ctx, ifs);
572 	if (token == NULL) {
573 		RWLOCK_EXIT(&ifs->ifs_ipf_tokens);
574 		return ESRCH;
575 	}
576 
577 	switch (iter.ili_type)
578 	{
579 	case IPLT_POOL :
580 		err = ip_pool_getnext(token, &iter, ifs);
581 		break;
582 	case IPLT_HASH :
583 		err = fr_htable_getnext(token, &iter, ifs);
584 		break;
585 	default :
586 #ifdef _KERNEL
587 		(void) printf("type=%d\n", iter.ili_type);
588 #endif
589 		err = EINVAL;
590 		break;
591 	}
592 	RWLOCK_EXIT(&ifs->ifs_ipf_tokens);
593 
594 	return err;
595 }
596 
597 
598 void ip_lookup_iterderef(type, data, ifs)
599 u_32_t type;
600 void *data;
601 ipf_stack_t *ifs;
602 {
603 	iplookupiterkey_t	key;
604 
605 	key.ilik_key = type;
606 
607 	if (key.ilik_unstr.ilik_ival != IPFGENITER_LOOKUP)
608 		return;
609 
610 	switch (key.ilik_unstr.ilik_type)
611 	{
612 	case IPLT_HASH :
613 		fr_htable_iterderef((u_int)key.ilik_unstr.ilik_otype,
614 				    (int)key.ilik_unstr.ilik_unit, data, ifs);
615 		break;
616 	case IPLT_POOL :
617 		ip_pool_iterderef((u_int)key.ilik_unstr.ilik_otype,
618 				  (int)key.ilik_unstr.ilik_unit, data, ifs);
619 		break;
620 	}
621 }
622 
623 
624 #else /* IPFILTER_LOOKUP */
625 
626 /*ARGSUSED*/
627 int ip_lookup_ioctl(data, cmd, mode, uid, ifs)
628 caddr_t data;
629 ioctlcmd_t cmd;
630 int mode, uid;
631 ipf_stack_t *ifs;
632 {
633 	return EIO;
634 }
635 #endif /* IPFILTER_LOOKUP */
636