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