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