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 /* ------------------------------------------------------------------------ */ 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 /* ------------------------------------------------------------------------ */ 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 /* ------------------------------------------------------------------------ */ 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 /* ------------------------------------------------------------------------ */ 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 /* ------------------------------------------------------------------------ */ 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 /* ------------------------------------------------------------------------ */ 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 /* ------------------------------------------------------------------------ */ 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 /* ------------------------------------------------------------------------ */ 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 /* ------------------------------------------------------------------------ */ 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 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 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 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*/ 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