1 /* 2 * Copyright (C) 2002-2003 by Darren Reed. 3 * 4 * See the IPFILTER.LICENCE file for details on licencing. 5 * 6 * Copyright 2006 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 /* END OF INCLUDES */ 68 69 #if !defined(lint) 70 static const char rcsid[] = "@(#)$Id: ip_lookup.c,v 2.35.2.7 2005/06/12 07:18:20 darrenr Exp $"; 71 #endif 72 73 #ifdef IPFILTER_LOOKUP 74 int ip_lookup_inited = 0; 75 76 static int iplookup_addnode __P((caddr_t)); 77 static int iplookup_delnode __P((caddr_t data)); 78 static int iplookup_addtable __P((caddr_t)); 79 static int iplookup_deltable __P((caddr_t)); 80 static int iplookup_stats __P((caddr_t)); 81 static int iplookup_flush __P((caddr_t)); 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() 92 { 93 94 if (ip_pool_init() == -1) 95 return -1; 96 97 RWLOCK_INIT(&ip_poolrw, "ip pool rwlock"); 98 99 ip_lookup_inited = 1; 100 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() 115 { 116 ip_pool_fini(); 117 fr_htable_unload(); 118 119 if (ip_lookup_inited == 1) { 120 RW_DESTROY(&ip_poolrw); 121 ip_lookup_inited = 0; 122 } 123 } 124 125 126 /* ------------------------------------------------------------------------ */ 127 /* Function: iplookup_ioctl */ 128 /* Returns: int - 0 = success, else error */ 129 /* Parameters: data(IO) - pointer to ioctl data to be copied to/from user */ 130 /* space. */ 131 /* cmd(I) - ioctl command number */ 132 /* mode(I) - file mode bits used with open */ 133 /* */ 134 /* Handle ioctl commands sent to the ioctl device. For the most part, this */ 135 /* involves just calling another function to handle the specifics of each */ 136 /* command. */ 137 /* ------------------------------------------------------------------------ */ 138 int ip_lookup_ioctl(data, cmd, mode) 139 caddr_t data; 140 ioctlcmd_t cmd; 141 int mode; 142 { 143 int err; 144 SPL_INT(s); 145 146 mode = mode; /* LINT */ 147 148 SPL_NET(s); 149 150 switch (cmd) 151 { 152 case SIOCLOOKUPADDNODE : 153 case SIOCLOOKUPADDNODEW : 154 WRITE_ENTER(&ip_poolrw); 155 err = iplookup_addnode(data); 156 RWLOCK_EXIT(&ip_poolrw); 157 break; 158 159 case SIOCLOOKUPDELNODE : 160 case SIOCLOOKUPDELNODEW : 161 WRITE_ENTER(&ip_poolrw); 162 err = iplookup_delnode(data); 163 RWLOCK_EXIT(&ip_poolrw); 164 break; 165 166 case SIOCLOOKUPADDTABLE : 167 WRITE_ENTER(&ip_poolrw); 168 err = iplookup_addtable(data); 169 RWLOCK_EXIT(&ip_poolrw); 170 break; 171 172 case SIOCLOOKUPDELTABLE : 173 WRITE_ENTER(&ip_poolrw); 174 err = iplookup_deltable(data); 175 RWLOCK_EXIT(&ip_poolrw); 176 break; 177 178 case SIOCLOOKUPSTAT : 179 case SIOCLOOKUPSTATW : 180 WRITE_ENTER(&ip_poolrw); 181 err = iplookup_stats(data); 182 RWLOCK_EXIT(&ip_poolrw); 183 break; 184 185 case SIOCLOOKUPFLUSH : 186 WRITE_ENTER(&ip_poolrw); 187 err = iplookup_flush(data); 188 RWLOCK_EXIT(&ip_poolrw); 189 break; 190 191 default : 192 err = EINVAL; 193 break; 194 } 195 SPL_X(s); 196 return err; 197 } 198 199 200 /* ------------------------------------------------------------------------ */ 201 /* Function: iplookup_addnode */ 202 /* Returns: int - 0 = success, else error */ 203 /* Parameters: data(I) - pointer to data from ioctl call */ 204 /* */ 205 /* Add a new data node to a lookup structure. First, check to see if the */ 206 /* parent structure refered to by name exists and if it does, then go on to */ 207 /* add a node to it. */ 208 /* ------------------------------------------------------------------------ */ 209 static int iplookup_addnode(data) 210 caddr_t data; 211 { 212 ip_pool_node_t node, *m; 213 iplookupop_t op; 214 iphtable_t *iph; 215 iphtent_t hte; 216 ip_pool_t *p; 217 int err; 218 219 err = 0; 220 BCOPYIN(data, &op, sizeof(op)); 221 op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; 222 223 switch (op.iplo_type) 224 { 225 case IPLT_POOL : 226 if (op.iplo_size != sizeof(node)) 227 return EINVAL; 228 229 err = COPYIN(op.iplo_struct, &node, sizeof(node)); 230 if (err != 0) 231 return EFAULT; 232 233 p = ip_pool_find(op.iplo_unit, op.iplo_name); 234 if (p == NULL) 235 return ESRCH; 236 237 /* 238 * add an entry to a pool - return an error if it already 239 * exists remove an entry from a pool - if it exists 240 * - in both cases, the pool *must* exist! 241 */ 242 m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask); 243 if (m) 244 return EEXIST; 245 err = ip_pool_insert(p, &node.ipn_addr, 246 &node.ipn_mask, node.ipn_info); 247 break; 248 249 case IPLT_HASH : 250 if (op.iplo_size != sizeof(hte)) 251 return EINVAL; 252 253 err = COPYIN(op.iplo_struct, &hte, sizeof(hte)); 254 if (err != 0) 255 return EFAULT; 256 257 iph = fr_findhtable(op.iplo_unit, op.iplo_name); 258 if (iph == NULL) 259 return ESRCH; 260 err = fr_addhtent(iph, &hte); 261 break; 262 263 default : 264 err = EINVAL; 265 break; 266 } 267 return err; 268 } 269 270 271 /* ------------------------------------------------------------------------ */ 272 /* Function: iplookup_delnode */ 273 /* Returns: int - 0 = success, else error */ 274 /* Parameters: data(I) - pointer to data from ioctl call */ 275 /* */ 276 /* Delete a node from a lookup table by first looking for the table it is */ 277 /* in and then deleting the entry that gets found. */ 278 /* ------------------------------------------------------------------------ */ 279 static int iplookup_delnode(data) 280 caddr_t data; 281 { 282 ip_pool_node_t node, *m; 283 iplookupop_t op; 284 iphtable_t *iph; 285 iphtent_t hte; 286 ip_pool_t *p; 287 int err; 288 289 err = 0; 290 BCOPYIN(data, &op, sizeof(op)); 291 292 op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; 293 294 switch (op.iplo_type) 295 { 296 case IPLT_POOL : 297 if (op.iplo_size != sizeof(node)) 298 return EINVAL; 299 300 err = COPYIN(op.iplo_struct, &node, sizeof(node)); 301 if (err != 0) 302 return EFAULT; 303 304 p = ip_pool_find(op.iplo_unit, op.iplo_name); 305 if (!p) 306 return ESRCH; 307 308 m = ip_pool_findeq(p, &node.ipn_addr, &node.ipn_mask); 309 if (m == NULL) 310 return ENOENT; 311 err = ip_pool_remove(p, m); 312 break; 313 314 case IPLT_HASH : 315 if (op.iplo_size != sizeof(hte)) 316 return EINVAL; 317 318 err = COPYIN(op.iplo_struct, &hte, sizeof(hte)); 319 if (err != 0) 320 return EFAULT; 321 322 iph = fr_findhtable(op.iplo_unit, op.iplo_name); 323 if (iph == NULL) 324 return ESRCH; 325 err = fr_delhtent(iph, &hte); 326 break; 327 328 default : 329 err = EINVAL; 330 break; 331 } 332 return err; 333 } 334 335 336 /* ------------------------------------------------------------------------ */ 337 /* Function: iplookup_addtable */ 338 /* Returns: int - 0 = success, else error */ 339 /* Parameters: data(I) - pointer to data from ioctl call */ 340 /* */ 341 /* Create a new lookup table, if one doesn't already exist using the name */ 342 /* for this one. */ 343 /* ------------------------------------------------------------------------ */ 344 static int iplookup_addtable(data) 345 caddr_t data; 346 { 347 iplookupop_t op; 348 int err; 349 350 err = 0; 351 BCOPYIN(data, &op, sizeof(op)); 352 353 op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; 354 355 switch (op.iplo_type) 356 { 357 case IPLT_POOL : 358 if (ip_pool_find(op.iplo_unit, op.iplo_name) != NULL) 359 err = EEXIST; 360 else 361 err = ip_pool_create(&op); 362 break; 363 364 case IPLT_HASH : 365 if (fr_findhtable(op.iplo_unit, op.iplo_name) != NULL) 366 err = EEXIST; 367 else 368 err = fr_newhtable(&op); 369 break; 370 371 default : 372 err = EINVAL; 373 break; 374 } 375 return err; 376 } 377 378 379 /* ------------------------------------------------------------------------ */ 380 /* Function: iplookup_deltable */ 381 /* Returns: int - 0 = success, else error */ 382 /* Parameters: data(I) - pointer to data from ioctl call */ 383 /* */ 384 /* Decodes ioctl request to remove a particular hash table or pool and */ 385 /* calls the relevant function to do the cleanup. */ 386 /* ------------------------------------------------------------------------ */ 387 static int iplookup_deltable(data) 388 caddr_t data; 389 { 390 iplookupop_t op; 391 int err; 392 393 BCOPYIN(data, &op, sizeof(op)); 394 op.iplo_name[sizeof(op.iplo_name) - 1] = '\0'; 395 396 if (op.iplo_arg & IPLT_ANON) 397 op.iplo_arg &= IPLT_ANON; 398 399 /* 400 * create a new pool - fail if one already exists with 401 * the same # 402 */ 403 switch (op.iplo_type) 404 { 405 case IPLT_POOL : 406 err = ip_pool_destroy(&op); 407 break; 408 409 case IPLT_HASH : 410 err = fr_removehtable(&op); 411 break; 412 413 default : 414 err = EINVAL; 415 break; 416 } 417 return err; 418 } 419 420 421 /* ------------------------------------------------------------------------ */ 422 /* Function: iplookup_stats */ 423 /* Returns: int - 0 = success, else error */ 424 /* Parameters: data(I) - pointer to data from ioctl call */ 425 /* */ 426 /* Copy statistical information from inside the kernel back to user space. */ 427 /* ------------------------------------------------------------------------ */ 428 static int iplookup_stats(data) 429 caddr_t data; 430 { 431 iplookupop_t op; 432 int err; 433 434 err = 0; 435 BCOPYIN(data, &op, sizeof(op)); 436 437 switch (op.iplo_type) 438 { 439 case IPLT_POOL : 440 err = ip_pool_statistics(&op); 441 break; 442 443 case IPLT_HASH : 444 err = fr_gethtablestat(&op); 445 break; 446 447 default : 448 err = EINVAL; 449 break; 450 } 451 return err; 452 } 453 454 455 /* ------------------------------------------------------------------------ */ 456 /* Function: iplookup_flush */ 457 /* Returns: int - 0 = success, else error */ 458 /* Parameters: data(I) - pointer to data from ioctl call */ 459 /* */ 460 /* A flush is called when we want to flush all the nodes from a particular */ 461 /* entry in the hash table/pool or want to remove all groups from those. */ 462 /* ------------------------------------------------------------------------ */ 463 static int iplookup_flush(data) 464 caddr_t data; 465 { 466 int err, unit, num, type; 467 iplookupflush_t flush; 468 469 err = 0; 470 BCOPYIN(data, &flush, sizeof(flush)); 471 472 flush.iplf_name[sizeof(flush.iplf_name) - 1] = '\0'; 473 474 unit = flush.iplf_unit; 475 if ((unit < 0 || unit > IPL_LOGMAX) && (unit != IPLT_ALL)) 476 return EINVAL; 477 478 type = flush.iplf_type; 479 err = EINVAL; 480 num = 0; 481 482 if (type == IPLT_POOL || type == IPLT_ALL) { 483 err = 0; 484 num = ip_pool_flush(&flush); 485 } 486 487 if (type == IPLT_HASH || type == IPLT_ALL) { 488 err = 0; 489 num += fr_flushhtable(&flush); 490 } 491 492 if (err == 0) { 493 flush.iplf_count = num; 494 err = COPYOUT(&flush, data, sizeof(flush)); 495 } 496 return err; 497 } 498 499 500 void ip_lookup_deref(type, ptr) 501 int type; 502 void *ptr; 503 { 504 if (ptr == NULL) 505 return; 506 507 WRITE_ENTER(&ip_poolrw); 508 switch (type) 509 { 510 case IPLT_POOL : 511 ip_pool_deref(ptr); 512 break; 513 514 case IPLT_HASH : 515 fr_derefhtable(ptr); 516 break; 517 } 518 RWLOCK_EXIT(&ip_poolrw); 519 } 520 521 522 #else /* IPFILTER_LOOKUP */ 523 524 /*ARGSUSED*/ 525 int ip_lookup_ioctl(data, cmd, mode) 526 caddr_t data; 527 ioctlcmd_t cmd; 528 int mode; 529 { 530 return EIO; 531 } 532 #endif /* IPFILTER_LOOKUP */ 533