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