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